PluginProbe
User Access Manager / 2.0.13
User Access Manager v2.0.13
2.3.20 2.3.19 2.3.18 2.3.17 2.3.16 2.3.15 2.3.14 2.3.13 trunk 0.6 0.6.1 0.6.2 0.7 0.7 Beta 0.7.0.1 0.8 0.8.0.1 0.8.0.2 0.9 0.9.1 0.9.1.1 0.9.1.2 0.9.1.3 0.9.1.4 1.0 All 136 releases
user-access-manager / ocular.phar

ocular.phar in User Access Manager 2.0.13, at ocular.phar

111,764 lines 3.7 MB
No matching file
Up and down to move Enter to open Esc to close
Raw Download Zip
1 #!/usr/bin/env php
2 <?php
3
4 /**
5 * Generated by Box.
6 *
7 * @link https://github.com/herrera-io/php-box/
8 */
9 Phar::mapPhar('default.phar');
10 require 'phar://' . __FILE__ . '/bin/ocular';__HALT_COMPILER(); ?>
11 E��6src/Scrutinizer/Ocular/Util/RepositoryIntrospector.php7���S7����(src/Scrutinizer/Ocular/Configuration.phpC���SC £�!src/Scrutinizer/Ocular/Ocular.php����S��⥯�=src/Scrutinizer/Ocular/Command/CodeCoverage/UploadCommand.php����S�B���4src/Scrutinizer/Ocular/Command/SelfUpdateCommand.phpy���Sy�-��%vendor/doctrine/annotations/README.mdB���SBPĶLvendor/doctrine/annotations/lib/Doctrine/Common/Annotations/CachedReader.php���S�2�Kvendor/doctrine/annotations/lib/Doctrine/Common/Annotations/TokenParser.php����S��>�Pvendor/doctrine/annotations/lib/Doctrine/Common/Annotations/AnnotationReader.php#+���S#+�L��Rvendor/doctrine/annotations/lib/Doctrine/Common/Annotations/AnnotationRegistry.php����S�cy�[�Vvendor/doctrine/annotations/lib/Doctrine/Common/Annotations/SimpleAnnotationReader.phpW���SW����Ovendor/doctrine/annotations/lib/Doctrine/Common/Annotations/FileCacheReader.php�!���S�!��w��Fvendor/doctrine/annotations/lib/Doctrine/Common/Annotations/Reader.phpp���Sp��b�Ivendor/doctrine/annotations/lib/Doctrine/Common/Annotations/PhpParser.phpq ���Sq ���Hvendor/doctrine/annotations/lib/Doctrine/Common/Annotations/DocLexer.php����S���)B�Jvendor/doctrine/annotations/lib/Doctrine/Common/Annotations/Annotation.php� ���S� o��c�Mvendor/doctrine/annotations/lib/Doctrine/Common/Annotations/IndexedReader.phpf���Sf�T@l�Ivendor/doctrine/annotations/lib/Doctrine/Common/Annotations/DocParser.php<����S<����M�[vendor/doctrine/annotations/lib/Doctrine/Common/Annotations/Annotation/IgnoreAnnotation.phpI���SI@·�Qvendor/doctrine/annotations/lib/Doctrine/Common/Annotations/Annotation/Target.php� ���S� _vbU�Uvendor/doctrine/annotations/lib/Doctrine/Common/Annotations/Annotation/Attributes.phpj���Sj��6�Tvendor/doctrine/annotations/lib/Doctrine/Common/Annotations/Annotation/Attribute.php����S����Svendor/doctrine/annotations/lib/Doctrine/Common/Annotations/Annotation/Required.php����S��M�Ovendor/doctrine/annotations/lib/Doctrine/Common/Annotations/Annotation/Enum.php$
12 ���S$
13 A}`ݶSvendor/doctrine/annotations/lib/Doctrine/Common/Annotations/AnnotationException.php)���S)9L�Uvendor/doctrine/annotations/tests/Doctrine/Tests/Common/Annotations/DocParserTest.php5����S5��:��Yvendor/doctrine/annotations/tests/Doctrine/Tests/Common/Annotations/Ticket/DCOM58Test.php" ���S" W��R�Yvendor/doctrine/annotations/tests/Doctrine/Tests/Common/Annotations/Ticket/DCOM55Test.phpB���SBPۦ��[vendor/doctrine/annotations/tests/Doctrine/Tests/Common/Annotations/Ticket/DCOM58Entity.phpa���Sa���Zvendor/doctrine/annotations/tests/Doctrine/Tests/Common/Annotations/AbstractReaderTest.php�Y���S�Y����bvendor/doctrine/annotations/tests/Doctrine/Tests/Common/Annotations/SimpleAnnotationReaderTest.php[ ���S[ �8H��Xvendor/doctrine/annotations/tests/Doctrine/Tests/Common/Annotations/CachedReaderTest.php����S��iж\vendor/doctrine/annotations/tests/Doctrine/Tests/Common/Annotations/AnnotationReaderTest.php����S��'��[vendor/doctrine/annotations/tests/Doctrine/Tests/Common/Annotations/FileCacheReaderTest.php����S���Rvendor/doctrine/annotations/tests/Doctrine/Tests/Common/Annotations/DummyClass.phpp���Sp��y��Wvendor/doctrine/annotations/tests/Doctrine/Tests/Common/Annotations/PerformanceTest.php=���S=k$:�Tvendor/doctrine/annotations/tests/Doctrine/Tests/Common/Annotations/DocLexerTest.php���So<�_�qvendor/doctrine/annotations/tests/Doctrine/Tests/Common/Annotations/Fixtures/AnnotationWithRequiredAttributes.php����S�&���{vendor/doctrine/annotations/tests/Doctrine/Tests/Common/Annotations/Fixtures/ClassWithInvalidAnnotationTargetAtProperty.php����S��+ @�Tvendor/doctrine/annotations/tests/Doctrine/Tests/Common/Annotations/Fixtures/Api.phpp���Sps���hvendor/doctrine/annotations/tests/Doctrine/Tests/Common/Annotations/Fixtures/AnnotationWithConstants.php���SnH�˶uvendor/doctrine/annotations/tests/Doctrine/Tests/Common/Annotations/Fixtures/ClassWithFullyQualifiedUseStatements.php;���S;�椶vvendor/doctrine/annotations/tests/Doctrine/Tests/Common/Annotations/Fixtures/InvalidAnnotationUsageButIgnoredClass.php����S�P��P�ovendor/doctrine/annotations/tests/Doctrine/Tests/Common/Annotations/Fixtures/ClassWithAnnotationWithVarType.php����S�Ӎa0�fvendor/doctrine/annotations/tests/Doctrine/Tests/Common/Annotations/Fixtures/AnnotationWithVarType.phpO���SO�1���]vendor/doctrine/annotations/tests/Doctrine/Tests/Common/Annotations/Fixtures/ClassDDC1660.php����S�9�*#�cvendor/doctrine/annotations/tests/Doctrine/Tests/Common/Annotations/Fixtures/SingleClassLOC1000.php�f���S�fE[�K�nvendor/doctrine/annotations/tests/Doctrine/Tests/Common/Annotations/Fixtures/MultipleImportsInUseStatement.php����S�W�)��wvendor/doctrine/annotations/tests/Doctrine/Tests/Common/Annotations/Fixtures/GlobalNamespacesPerFileWithClassAsLast.php���Sr�=J�wvendor/doctrine/annotations/tests/Doctrine/Tests/Common/Annotations/Fixtures/EqualNamespacesPerFileWithClassAsFirst.phpn���Sn��g��gvendor/doctrine/annotations/tests/Doctrine/Tests/Common/Annotations/Fixtures/AnnotationTargetMethod.php����S����hvendor/doctrine/annotations/tests/Doctrine/Tests/Common/Annotations/Fixtures/ClassWithAnnotationEnum.php����S��vt߶lvendor/doctrine/annotations/tests/Doctrine/Tests/Common/Annotations/Fixtures/InvalidAnnotationUsageClass.php|���S|R�7��]vendor/doctrine/annotations/tests/Doctrine/Tests/Common/Annotations/Fixtures/NoAnnotation.phpS���SS��U�_vendor/doctrine/annotations/tests/Doctrine/Tests/Common/Annotations/Fixtures/AnnotationEnum.php3���S3h�j��ivendor/doctrine/annotations/tests/Doctrine/Tests/Common/Annotations/Fixtures/AnnotationWithAttributes.php� ���S� i�D�kvendor/doctrine/annotations/tests/Doctrine/Tests/Common/Annotations/Fixtures/AnnotationTargetAnnotation.php����S���AĶvvendor/doctrine/annotations/tests/Doctrine/Tests/Common/Annotations/Fixtures/EqualNamespacesPerFileWithClassAsLast.phpl���Sl��O�pvendor/doctrine/annotations/tests/Doctrine/Tests/Common/Annotations/Fixtures/AnnotationWithTargetSyntaxError.php����S��F����vendor/doctrine/annotations/tests/Doctrine/Tests/Common/Annotations/Fixtures/AnnotationWithRequiredAttributesWithoutContructor.phpx���Sx�K�6�fvendor/doctrine/annotations/tests/Doctrine/Tests/Common/Annotations/Fixtures/AnnotationTargetClass.php����S�K/�ovendor/doctrine/annotations/tests/Doctrine/Tests/Common/Annotations/Fixtures/ClassWithValidAnnotationTarget.phpZ���SZ�0��mvendor/doctrine/annotations/tests/Doctrine/Tests/Common/Annotations/Fixtures/AnnotationEnumLiteralInvalid.php-���S-����fvendor/doctrine/annotations/tests/Doctrine/Tests/Common/Annotations/Fixtures/MultipleClassesInFile.php����S�� �=�dvendor/doctrine/annotations/tests/Doctrine/Tests/Common/Annotations/Fixtures/AnnotationTargetAll.php����S�E|H�avendor/doctrine/annotations/tests/Doctrine/Tests/Common/Annotations/Fixtures/ClassWithRequire.php,���S,�%�nvendor/doctrine/annotations/tests/Doctrine/Tests/Common/Annotations/Fixtures/NamespaceAndClassCommentedOut.phpR���SRE�f��pvendor/doctrine/annotations/tests/Doctrine/Tests/Common/Annotations/Fixtures/NamespaceWithClosureDeclaration.phpU���SU\�&�yvendor/doctrine/annotations/tests/Doctrine/Tests/Common/Annotations/Fixtures/ClassWithAnnotationWithTargetSyntaxError.php����S�֗�fvendor/doctrine/annotations/tests/Doctrine/Tests/Common/Annotations/Fixtures/IntefaceWithConstants.php����S�����cvendor/doctrine/annotations/tests/Doctrine/Tests/Common/Annotations/Fixtures/NonNamespacedClass.php����S�$����xvendor/doctrine/annotations/tests/Doctrine/Tests/Common/Annotations/Fixtures/ClassWithInvalidAnnotationTargetAtClass.phpG���SGet��fvendor/doctrine/annotations/tests/Doctrine/Tests/Common/Annotations/Fixtures/AnnotationEnumLiteral.php����S�=�m�yvendor/doctrine/annotations/tests/Doctrine/Tests/Common/Annotations/Fixtures/ClassWithInvalidAnnotationTargetAtMethod.php[���S[?�4�cvendor/doctrine/annotations/tests/Doctrine/Tests/Common/Annotations/Fixtures/ClassWithConstants.php����S�����avendor/doctrine/annotations/tests/Doctrine/Tests/Common/Annotations/Fixtures/ClassWithClosure.php����S�� V�{vendor/doctrine/annotations/tests/Doctrine/Tests/Common/Annotations/Fixtures/DifferentNamespacesPerFileWithClassAsFirst.php����S���� �zvendor/doctrine/annotations/tests/Doctrine/Tests/Common/Annotations/Fixtures/DifferentNamespacesPerFileWithClassAsLast.php����S��!\�ovendor/doctrine/annotations/tests/Doctrine/Tests/Common/Annotations/Fixtures/AnnotationTargetPropertyMethod.php����S����^vendor/doctrine/annotations/tests/Doctrine/Tests/Common/Annotations/Fixtures/TestInterface.php����S����j�cvendor/doctrine/annotations/tests/Doctrine/Tests/Common/Annotations/Fixtures/Annotation/Version.php����S��Э��avendor/doctrine/annotations/tests/Doctrine/Tests/Common/Annotations/Fixtures/Annotation/Route.php����S�({�϶dvendor/doctrine/annotations/tests/Doctrine/Tests/Common/Annotations/Fixtures/Annotation/Template.php���S��Aضbvendor/doctrine/annotations/tests/Doctrine/Tests/Common/Annotations/Fixtures/Annotation/Secure.phpS���SSׯ�t�dvendor/doctrine/annotations/tests/Doctrine/Tests/Common/Annotations/Fixtures/Annotation/Autoload.phpr���Sr,<�qvendor/doctrine/annotations/tests/Doctrine/Tests/Common/Annotations/Fixtures/Annotation/AnnotWithDefaultValue.php����S�n�GW�xvendor/doctrine/annotations/tests/Doctrine/Tests/Common/Annotations/Fixtures/GlobalNamespacesPerFileWithClassAsFirst.php ���S �d˶[vendor/doctrine/annotations/tests/Doctrine/Tests/Common/Annotations/Fixtures/Controller.php�)���S�)��r�mvendor/doctrine/annotations/tests/Doctrine/Tests/Common/Annotations/Fixtures/NamespacedSingleClassLOC1000.phpg���Sg�)0��fvendor/doctrine/annotations/tests/Doctrine/Tests/Common/Annotations/Fixtures/AnnotationEnumInvalid.php����S��j�޶Uvendor/doctrine/annotations/tests/Doctrine/Tests/Common/Annotations/PhpParserTest.php����S�BV
14 <�Zvendor/doctrine/annotations/tests/Doctrine/Tests/Common/Annotations/TopLevelAnnotation.phpw���Sww�I�Evendor/doctrine/annotations/tests/Doctrine/Tests/DoctrineTestCase.php����S�x�Oy�=vendor/doctrine/annotations/tests/Doctrine/Tests/TestInit.php����S���)vendor/doctrine/annotations/composer.json����S�M䲳�,vendor/doctrine/annotations/phpunit.xml.dist7���S7_P(�vendor/doctrine/lexer/README.md����S�X���Avendor/doctrine/lexer/lib/Doctrine/Common/Lexer/AbstractLexer.phpK���SK��Ok�#vendor/doctrine/lexer/composer.json����S�Ʌ�Ķ"vendor/guzzle/guzzle/phar-stub.phpQ���SQ^ib7�vendor/guzzle/guzzle/README.md #���S #@����vendor/guzzle/guzzle/LICENSEW���SW�[� �=vendor/guzzle/guzzle/src/Guzzle/Cache/CacheAdapterFactory.php_���S_�'6,�?vendor/guzzle/guzzle/src/Guzzle/Cache/CacheAdapterInterface.php����S����:vendor/guzzle/guzzle/src/Guzzle/Cache/NullCacheAdapter.php���S���=vendor/guzzle/guzzle/src/Guzzle/Cache/ClosureCacheAdapter.php^���S^���ζ9vendor/guzzle/guzzle/src/Guzzle/Cache/Zf2CacheAdapter.php����S��I�̶>vendor/guzzle/guzzle/src/Guzzle/Cache/AbstractCacheAdapter.phpG���SG!��3vendor/guzzle/guzzle/src/Guzzle/Cache/composer.json����S�I�0e�9vendor/guzzle/guzzle/src/Guzzle/Cache/Zf1CacheAdapter.php����S�Z㍼�>vendor/guzzle/guzzle/src/Guzzle/Cache/DoctrineCacheAdapter.phpK���SK�;#��:vendor/guzzle/guzzle/src/Guzzle/Log/AbstractLogAdapter.php ���S p��-�7vendor/guzzle/guzzle/src/Guzzle/Log/ArrayLogAdapter.phpg���Sgx�c�9vendor/guzzle/guzzle/src/Guzzle/Log/ClosureLogAdapter.phpG���SG��#p�5vendor/guzzle/guzzle/src/Guzzle/Log/PsrLogAdapter.php^���S^�����8vendor/guzzle/guzzle/src/Guzzle/Log/MessageFormatter.php����S��B�5vendor/guzzle/guzzle/src/Guzzle/Log/Zf1LogAdapter.php����S���Yڶ;vendor/guzzle/guzzle/src/Guzzle/Log/LogAdapterInterface.php����S��\:�5vendor/guzzle/guzzle/src/Guzzle/Log/Zf2LogAdapter.php����S�>'���1vendor/guzzle/guzzle/src/Guzzle/Log/composer.json����S�<��:�9vendor/guzzle/guzzle/src/Guzzle/Log/MonologLogAdapter.php����S�LbȊ�@vendor/guzzle/guzzle/src/Guzzle/Iterator/MethodProxyIterator.phpb���Sb]���2vendor/guzzle/guzzle/src/Guzzle/Iterator/README.md����S��ɧQ�<vendor/guzzle/guzzle/src/Guzzle/Iterator/ChunkedIterator.php���SGZ���8vendor/guzzle/guzzle/src/Guzzle/Iterator/MapIterator.phpT���STR`1q�;vendor/guzzle/guzzle/src/Guzzle/Iterator/AppendIterator.php����S�����6vendor/guzzle/guzzle/src/Guzzle/Iterator/composer.json����S��r[v�;vendor/guzzle/guzzle/src/Guzzle/Iterator/FilterIterator.php����S�ڬ���Bvendor/guzzle/guzzle/src/Guzzle/Plugin/CurlAuth/CurlAuthPlugin.phpl���Sl˞��=vendor/guzzle/guzzle/src/Guzzle/Plugin/CurlAuth/composer.json����S��6�:vendor/guzzle/guzzle/src/Guzzle/Plugin/Async/composer.json����S���E �<vendor/guzzle/guzzle/src/Guzzle/Plugin/Async/AsyncPlugin.phpx
15 ���Sx
16 ���Fvendor/guzzle/guzzle/src/Guzzle/Plugin/Md5/CommandContentMd5Plugin.php����S��'f��Avendor/guzzle/guzzle/src/Guzzle/Plugin/Md5/Md5ValidatorPlugin.php� ���S� �j�8vendor/guzzle/guzzle/src/Guzzle/Plugin/Md5/composer.json����S��m���Hvendor/guzzle/guzzle/src/Guzzle/Plugin/Cache/DefaultCacheKeyProvider.php����S���V��Dvendor/guzzle/guzzle/src/Guzzle/Plugin/Cache/DefaultCacheStorage.php2���S2���@�Ivendor/guzzle/guzzle/src/Guzzle/Plugin/Cache/CallbackCanCacheStrategy.php����S�,\�p�Jvendor/guzzle/guzzle/src/Guzzle/Plugin/Cache/CanCacheStrategyInterface.php����S���[�Avendor/guzzle/guzzle/src/Guzzle/Plugin/Cache/DenyRevalidation.php����S���<vendor/guzzle/guzzle/src/Guzzle/Plugin/Cache/CachePlugin.php:3���S:3P��Fvendor/guzzle/guzzle/src/Guzzle/Plugin/Cache/CacheStorageInterface.php����S�]�9�Jvendor/guzzle/guzzle/src/Guzzle/Plugin/Cache/CacheKeyProviderInterface.php����S��k�˶Hvendor/guzzle/guzzle/src/Guzzle/Plugin/Cache/DefaultCanCacheStrategy.php����S������Dvendor/guzzle/guzzle/src/Guzzle/Plugin/Cache/DefaultRevalidation.php����S��X 7�Avendor/guzzle/guzzle/src/Guzzle/Plugin/Cache/SkipRevalidation.php����S�Nt�:vendor/guzzle/guzzle/src/Guzzle/Plugin/Cache/composer.json����S��öFvendor/guzzle/guzzle/src/Guzzle/Plugin/Cache/RevalidationInterface.phpN���SN��D��8vendor/guzzle/guzzle/src/Guzzle/Plugin/Log/LogPlugin.php���S�Ul��8vendor/guzzle/guzzle/src/Guzzle/Plugin/Log/composer.json����S�<�i�Fvendor/guzzle/guzzle/src/Guzzle/Plugin/Backoff/CurlBackoffStrategy.php����S�b�7%�Mvendor/guzzle/guzzle/src/Guzzle/Plugin/Backoff/ExponentialBackoffStrategy.php����S�m�hS�@vendor/guzzle/guzzle/src/Guzzle/Plugin/Backoff/BackoffLogger.php ���S (�*��Nvendor/guzzle/guzzle/src/Guzzle/Plugin/Backoff/ReasonPhraseBackoffStrategy.php����S�� sW�@vendor/guzzle/guzzle/src/Guzzle/Plugin/Backoff/BackoffPlugin.php ���S ��WZ�Hvendor/guzzle/guzzle/src/Guzzle/Plugin/Backoff/LinearBackoffStrategy.php����S�T�#�Kvendor/guzzle/guzzle/src/Guzzle/Plugin/Backoff/TruncatedBackoffStrategy.php����S��AQp�Kvendor/guzzle/guzzle/src/Guzzle/Plugin/Backoff/BackoffStrategyInterface.php����S�X�|�Fvendor/guzzle/guzzle/src/Guzzle/Plugin/Backoff/HttpBackoffStrategy.php����S�#Ǧ�Svendor/guzzle/guzzle/src/Guzzle/Plugin/Backoff/AbstractErrorCodeBackoffStrategy.php���S�|�Jvendor/guzzle/guzzle/src/Guzzle/Plugin/Backoff/ConstantBackoffStrategy.php����S�
17 j�v�<vendor/guzzle/guzzle/src/Guzzle/Plugin/Backoff/composer.json����S�"��Jvendor/guzzle/guzzle/src/Guzzle/Plugin/Backoff/AbstractBackoffStrategy.phpI ���SI ��Z4�Jvendor/guzzle/guzzle/src/Guzzle/Plugin/Backoff/CallbackBackoffStrategy.php����S��>��<vendor/guzzle/guzzle/src/Guzzle/Plugin/Oauth/OauthPlugin.php"$���S"$�:W�:vendor/guzzle/guzzle/src/Guzzle/Plugin/Oauth/composer.json����S�>7��Ivendor/guzzle/guzzle/src/Guzzle/Plugin/Cookie/CookieJar/FileCookieJar.php����S�t2L�Nvendor/guzzle/guzzle/src/Guzzle/Plugin/Cookie/CookieJar/CookieJarInterface.phpj ���Sj 4`۶Jvendor/guzzle/guzzle/src/Guzzle/Plugin/Cookie/CookieJar/ArrayCookieJar.php4���S4�YO�8vendor/guzzle/guzzle/src/Guzzle/Plugin/Cookie/Cookie.php�-���S�-�ń�>vendor/guzzle/guzzle/src/Guzzle/Plugin/Cookie/CookiePlugin.php����S�L"]��Rvendor/guzzle/guzzle/src/Guzzle/Plugin/Cookie/Exception/InvalidCookieException.php����S�!m|��;vendor/guzzle/guzzle/src/Guzzle/Plugin/Cookie/composer.json����S���R�4vendor/guzzle/guzzle/src/Guzzle/Plugin/composer.jsonT���STo&��:vendor/guzzle/guzzle/src/Guzzle/Plugin/Mock/MockPlugin.php����S�*�� �9vendor/guzzle/guzzle/src/Guzzle/Plugin/Mock/composer.json����S�~=�x�Lvendor/guzzle/guzzle/src/Guzzle/Plugin/ErrorResponse/ErrorResponsePlugin.php�
18 ���S�
19 ���D�Xvendor/guzzle/guzzle/src/Guzzle/Plugin/ErrorResponse/ErrorResponseExceptionInterface.phpV���SV|�F��Yvendor/guzzle/guzzle/src/Guzzle/Plugin/ErrorResponse/Exception/ErrorResponseException.php����S���ҙ�Bvendor/guzzle/guzzle/src/Guzzle/Plugin/ErrorResponse/composer.json����S�[y��@vendor/guzzle/guzzle/src/Guzzle/Plugin/History/HistoryPlugin.phpQ���SQDS�+�<vendor/guzzle/guzzle/src/Guzzle/Plugin/History/composer.json����S��:��2vendor/guzzle/guzzle/src/Guzzle/Common/Version.php����S�ތ� �Avendor/guzzle/guzzle/src/Guzzle/Common/HasDispatcherInterface.phpN���SN�3��0vendor/guzzle/guzzle/src/Guzzle/Common/Event.php(���S(��k۶>vendor/guzzle/guzzle/src/Guzzle/Common/FromConfigInterface.php����S���1r�Evendor/guzzle/guzzle/src/Guzzle/Common/Exception/RuntimeException.phpz���Szh^�w�Mvendor/guzzle/guzzle/src/Guzzle/Common/Exception/InvalidArgumentException.php����S��VǶHvendor/guzzle/guzzle/src/Guzzle/Common/Exception/ExceptionCollection.php����S�Xz��Kvendor/guzzle/guzzle/src/Guzzle/Common/Exception/BadMethodCallException.php����S��qS��Mvendor/guzzle/guzzle/src/Guzzle/Common/Exception/UnexpectedValueException.php����S��e���Dvendor/guzzle/guzzle/src/Guzzle/Common/Exception/GuzzleException.phpd���Sd�ض5vendor/guzzle/guzzle/src/Guzzle/Common/Collection.php-���S-��E�;vendor/guzzle/guzzle/src/Guzzle/Common/ToArrayInterface.php����S���N�@vendor/guzzle/guzzle/src/Guzzle/Common/AbstractHasDispatcher.php����S�%�;��4vendor/guzzle/guzzle/src/Guzzle/Common/composer.json����S��>x��Avendor/guzzle/guzzle/src/Guzzle/Service/ConfigLoaderInterface.php����S�VQ�h�Jvendor/guzzle/guzzle/src/Guzzle/Service/Description/ValidatorInterface.php����S��b��Pvendor/guzzle/guzzle/src/Guzzle/Service/Description/ServiceDescriptionLoader.php� ���S� %��@�Avendor/guzzle/guzzle/src/Guzzle/Service/Description/Parameter.php c���S c�*�ԶAvendor/guzzle/guzzle/src/Guzzle/Service/Description/Operation.phpu=���Su=����Jvendor/guzzle/guzzle/src/Guzzle/Service/Description/ServiceDescription.php����S���#ŶGvendor/guzzle/guzzle/src/Guzzle/Service/Description/SchemaValidator.phpG.���SG.ʻ�۶Gvendor/guzzle/guzzle/src/Guzzle/Service/Description/SchemaFormatter.php����S�H���Jvendor/guzzle/guzzle/src/Guzzle/Service/Description/OperationInterface.php����S�e� �Svendor/guzzle/guzzle/src/Guzzle/Service/Description/ServiceDescriptionInterface.php����S�/S��;vendor/guzzle/guzzle/src/Guzzle/Service/ClientInterface.php�
20 ���S�
21 ���2vendor/guzzle/guzzle/src/Guzzle/Service/Client.phpG$���SG$�.eY�?vendor/guzzle/guzzle/src/Guzzle/Service/CachingConfigLoader.php����S��Nvendor/guzzle/guzzle/src/Guzzle/Service/Resource/ResourceIteratorInterface.php���S�2�P�Uvendor/guzzle/guzzle/src/Guzzle/Service/Resource/ResourceIteratorFactoryInterface.php���S癤ѶOvendor/guzzle/guzzle/src/Guzzle/Service/Resource/MapResourceIteratorFactory.php����S������Evendor/guzzle/guzzle/src/Guzzle/Service/Resource/ResourceIterator.php\���S\K�˶Qvendor/guzzle/guzzle/src/Guzzle/Service/Resource/ResourceIteratorClassFactory.php����S�q���:vendor/guzzle/guzzle/src/Guzzle/Service/Resource/Model.php ���S "��4�Uvendor/guzzle/guzzle/src/Guzzle/Service/Resource/CompositeResourceIteratorFactory.php����S��쾒�Qvendor/guzzle/guzzle/src/Guzzle/Service/Resource/ResourceIteratorApplyBatched.phpS ���SS
22 ֜��Tvendor/guzzle/guzzle/src/Guzzle/Service/Resource/AbstractResourceIteratorFactory.php���S`�Ivendor/guzzle/guzzle/src/Guzzle/Service/Exception/ValidationException.php���Sb�d�Nvendor/guzzle/guzzle/src/Guzzle/Service/Exception/CommandTransferException.php� ���S� V��϶Qvendor/guzzle/guzzle/src/Guzzle/Service/Exception/DescriptionBuilderException.php����S�ƣ��Mvendor/guzzle/guzzle/src/Guzzle/Service/Exception/ServiceBuilderException.php����S���NζLvendor/guzzle/guzzle/src/Guzzle/Service/Exception/ResponseClassException.php����S�#��N�Nvendor/guzzle/guzzle/src/Guzzle/Service/Exception/ServiceNotFoundException.phpn���Sn�S�h�Fvendor/guzzle/guzzle/src/Guzzle/Service/Exception/CommandException.php����S���կ�Yvendor/guzzle/guzzle/src/Guzzle/Service/Exception/InconsistentClientTransferException.php����S�y ���5vendor/guzzle/guzzle/src/Guzzle/Service/composer.json���S<��x�Bvendor/guzzle/guzzle/src/Guzzle/Service/Builder/ServiceBuilder.php���S����Hvendor/guzzle/guzzle/src/Guzzle/Service/Builder/ServiceBuilderLoader.phpD ���SD ��_�Kvendor/guzzle/guzzle/src/Guzzle/Service/Builder/ServiceBuilderInterface.php����S��S�)�Bvendor/guzzle/guzzle/src/Guzzle/Service/Command/ClosureCommand.php���S���.�Lvendor/guzzle/guzzle/src/Guzzle/Service/Command/DefaultRequestSerializer.php����S�#�,�Jvendor/guzzle/guzzle/src/Guzzle/Service/Command/ResponseClassInterface.php����S�s�o�Kvendor/guzzle/guzzle/src/Guzzle/Service/Command/ResponseParserInterface.php����S�w��{�Fvendor/guzzle/guzzle/src/Guzzle/Service/Command/Factory/MapFactory.php���S� ���Lvendor/guzzle/guzzle/src/Guzzle/Service/Command/Factory/CompositeFactory.php���Sj1̶Uvendor/guzzle/guzzle/src/Guzzle/Service/Command/Factory/ServiceDescriptionFactory.phpm���Sm����Pvendor/guzzle/guzzle/src/Guzzle/Service/Command/Factory/ConcreteClassFactory.phpE���SE��=�Hvendor/guzzle/guzzle/src/Guzzle/Service/Command/Factory/AliasFactory.php`���S`d���Lvendor/guzzle/guzzle/src/Guzzle/Service/Command/Factory/FactoryInterface.php����S�ϠYc�Ivendor/guzzle/guzzle/src/Guzzle/Service/Command/DefaultResponseParser.php����S��� x�Dvendor/guzzle/guzzle/src/Guzzle/Service/Command/CommandInterface.php� ���S� %����Cvendor/guzzle/guzzle/src/Guzzle/Service/Command/AbstractCommand.php;/���S;/�Vy��Dvendor/guzzle/guzzle/src/Guzzle/Service/Command/OperationCommand.php� ���S� �Y���Nvendor/guzzle/guzzle/src/Guzzle/Service/Command/RequestSerializerInterface.php����S�ÖEk�Vvendor/guzzle/guzzle/src/Guzzle/Service/Command/LocationVisitor/Request/XmlVisitor.php����S���r�cvendor/guzzle/guzzle/src/Guzzle/Service/Command/LocationVisitor/Request/RequestVisitorInterface.php7���S7{�Hƶ\vendor/guzzle/guzzle/src/Guzzle/Service/Command/LocationVisitor/Request/PostFieldVisitor.php ���S ˱�ԶWvendor/guzzle/guzzle/src/Guzzle/Service/Command/LocationVisitor/Request/BodyVisitor.php� ���S� Y4��[vendor/guzzle/guzzle/src/Guzzle/Service/Command/LocationVisitor/Request/PostFileVisitor.php����S���$p�Xvendor/guzzle/guzzle/src/Guzzle/Service/Command/LocationVisitor/Request/QueryVisitor.php���S=����bvendor/guzzle/guzzle/src/Guzzle/Service/Command/LocationVisitor/Request/AbstractRequestVisitor.php����S���Zv�Wvendor/guzzle/guzzle/src/Guzzle/Service/Command/LocationVisitor/Request/JsonVisitor.php����S���a
23 �Yvendor/guzzle/guzzle/src/Guzzle/Service/Command/LocationVisitor/Request/HeaderVisitor.php����S� ϶q�_vendor/guzzle/guzzle/src/Guzzle/Service/Command/LocationVisitor/Request/ResponseBodyVisitor.php����S�V����Wvendor/guzzle/guzzle/src/Guzzle/Service/Command/LocationVisitor/Response/XmlVisitor.phpr���Sr�4E��^vendor/guzzle/guzzle/src/Guzzle/Service/Command/LocationVisitor/Response/StatusCodeVisitor.phpB���SB�x�@�dvendor/guzzle/guzzle/src/Guzzle/Service/Command/LocationVisitor/Response/AbstractResponseVisitor.phpf���Sf}��M�Xvendor/guzzle/guzzle/src/Guzzle/Service/Command/LocationVisitor/Response/BodyVisitor.php3���S3����`vendor/guzzle/guzzle/src/Guzzle/Service/Command/LocationVisitor/Response/ReasonPhraseVisitor.phpH���SH��1�evendor/guzzle/guzzle/src/Guzzle/Service/Command/LocationVisitor/Response/ResponseVisitorInterface.php2���S2��[�Xvendor/guzzle/guzzle/src/Guzzle/Service/Command/LocationVisitor/Response/JsonVisitor.php� ���S� F�z��Zvendor/guzzle/guzzle/src/Guzzle/Service/Command/LocationVisitor/Response/HeaderVisitor.php���S�տ�Tvendor/guzzle/guzzle/src/Guzzle/Service/Command/LocationVisitor/VisitorFlyweight.php����S���Q�Kvendor/guzzle/guzzle/src/Guzzle/Service/Command/OperationResponseParser.php����S�R��Lvendor/guzzle/guzzle/src/Guzzle/Service/Command/CreateResponseClassEvent.php<���S< 8+�@vendor/guzzle/guzzle/src/Guzzle/Service/AbstractConfigLoader.phpq���Sq��7�@vendor/guzzle/guzzle/src/Guzzle/Batch/AbstractBatchDecorator.php����S��?��8vendor/guzzle/guzzle/src/Guzzle/Batch/BatchInterface.phpM���SMƿs|�Avendor/guzzle/guzzle/src/Guzzle/Batch/ExceptionBufferingBatch.php����S��E$��:vendor/guzzle/guzzle/src/Guzzle/Batch/BatchSizeDivisor.phpk���Skd�j��>vendor/guzzle/guzzle/src/Guzzle/Batch/BatchCommandTransfer.php����S���2�8vendor/guzzle/guzzle/src/Guzzle/Batch/NotifyingBatch.php����S��j��7vendor/guzzle/guzzle/src/Guzzle/Batch/FlushingBatch.php����S�ۆ�P�/vendor/guzzle/guzzle/src/Guzzle/Batch/Batch.php� ���S� �s��?vendor/guzzle/guzzle/src/Guzzle/Batch/BatchDivisorInterface.php����S���~��Jvendor/guzzle/guzzle/src/Guzzle/Batch/Exception/BatchTransferException.php�
24 ���S�
25 &���=vendor/guzzle/guzzle/src/Guzzle/Batch/BatchClosureDivisor.phpt���StzHF�6vendor/guzzle/guzzle/src/Guzzle/Batch/BatchBuilder.php����S��;Ͷ@vendor/guzzle/guzzle/src/Guzzle/Batch/BatchTransferInterface.php���S��#T�3vendor/guzzle/guzzle/src/Guzzle/Batch/composer.json/���S/V�y�6vendor/guzzle/guzzle/src/Guzzle/Batch/HistoryBatch.php����S�`B2�>vendor/guzzle/guzzle/src/Guzzle/Batch/BatchClosureTransfer.php����S���ֶ>vendor/guzzle/guzzle/src/Guzzle/Batch/BatchRequestTransfer.php����S��q;�Avendor/guzzle/guzzle/src/Guzzle/Parser/Url/UrlParserInterface.php���S�"��8vendor/guzzle/guzzle/src/Guzzle/Parser/Url/UrlParser.php����S�loV2�9vendor/guzzle/guzzle/src/Guzzle/Parser/ParserRegistry.php����S����Bvendor/guzzle/guzzle/src/Guzzle/Parser/UriTemplate/UriTemplate.php/���S/��Ql�Fvendor/guzzle/guzzle/src/Guzzle/Parser/UriTemplate/PeclUriTemplate.php����S���QѶKvendor/guzzle/guzzle/src/Guzzle/Parser/UriTemplate/UriTemplateInterface.php����S�[��Gvendor/guzzle/guzzle/src/Guzzle/Parser/Cookie/CookieParserInterface.php����S��=�>vendor/guzzle/guzzle/src/Guzzle/Parser/Cookie/CookieParser.php� ���S� |�a�Ivendor/guzzle/guzzle/src/Guzzle/Parser/Message/MessageParserInterface.php����S�(5�;�Hvendor/guzzle/guzzle/src/Guzzle/Parser/Message/PeclHttpMessageParser.php����S����@vendor/guzzle/guzzle/src/Guzzle/Parser/Message/MessageParser.php ���S ��,�Hvendor/guzzle/guzzle/src/Guzzle/Parser/Message/AbstractMessageParser.php����S��J2�4vendor/guzzle/guzzle/src/Guzzle/Parser/composer.json����S�;�`��8vendor/guzzle/guzzle/src/Guzzle/Http/Curl/CurlHandle.php�;���S�;�k��@vendor/guzzle/guzzle/src/Guzzle/Http/Curl/CurlMultiInterface.php=���S=�њO�9vendor/guzzle/guzzle/src/Guzzle/Http/Curl/CurlVersion.php����S���\�<vendor/guzzle/guzzle/src/Guzzle/Http/Curl/CurlMultiProxy.phpG���SG|�~��=vendor/guzzle/guzzle/src/Guzzle/Http/Curl/RequestMediator.php]���S]T�š�7vendor/guzzle/guzzle/src/Guzzle/Http/Curl/CurlMulti.php�1���S�1�1��Hvendor/guzzle/guzzle/src/Guzzle/Http/QueryAggregator/CommaAggregator.php���S�^<ͶFvendor/guzzle/guzzle/src/Guzzle/Http/QueryAggregator/PhpAggregator.phpt���St�$��Lvendor/guzzle/guzzle/src/Guzzle/Http/QueryAggregator/DuplicateAggregator.php<���S<9�|\�Qvendor/guzzle/guzzle/src/Guzzle/Http/QueryAggregator/QueryAggregatorInterface.php����S�)��2vendor/guzzle/guzzle/src/Guzzle/Http/Mimetypes.php�����S����<vendor/guzzle/guzzle/src/Guzzle/Http/ReadLimitEntityBody.php~ ���S~ S왶4vendor/guzzle/guzzle/src/Guzzle/Http/QueryString.php����S����Q�7vendor/guzzle/guzzle/src/Guzzle/Http/RedirectPlugin.php '���S '���w�:vendor/guzzle/guzzle/src/Guzzle/Http/CachingEntityBody.phpz���Sz��b�,vendor/guzzle/guzzle/src/Guzzle/Http/Url.phps5���Ss5
26 @�O�8vendor/guzzle/guzzle/src/Guzzle/Http/ClientInterface.php�&���S�&P g5�5vendor/guzzle/guzzle/src/Guzzle/Http/StaticClient.php'���S'�Z�!�Dvendor/guzzle/guzzle/src/Guzzle/Http/AbstractEntityBodyDecorator.php����S��a��/vendor/guzzle/guzzle/src/Guzzle/Http/Client.phpA���SAѷ� �8vendor/guzzle/guzzle/src/Guzzle/Http/Message/Request.php�K���S�K�����Avendor/guzzle/guzzle/src/Guzzle/Http/Message/RequestInterface.php7!���S7!��Օ�@vendor/guzzle/guzzle/src/Guzzle/Http/Message/AbstractMessage.php#���S#�C}#�Gvendor/guzzle/guzzle/src/Guzzle/Http/Message/EntityEnclosingRequest.php����S���4�9vendor/guzzle/guzzle/src/Guzzle/Http/Message/PostFile.phpS
27 ���SS
28 �WT�7vendor/guzzle/guzzle/src/Guzzle/Http/Message/Header.php����S��c9a�<vendor/guzzle/guzzle/src/Guzzle/Http/Message/Header/Link.php����S������Evendor/guzzle/guzzle/src/Guzzle/Http/Message/Header/HeaderFactory.php����S��┶Dvendor/guzzle/guzzle/src/Guzzle/Http/Message/Header/CacheControl.php�
29 ���S�
30 ��O�Gvendor/guzzle/guzzle/src/Guzzle/Http/Message/Header/HeaderInterface.phpP���SP�(��Hvendor/guzzle/guzzle/src/Guzzle/Http/Message/Header/HeaderCollection.php>���S>_�G�Nvendor/guzzle/guzzle/src/Guzzle/Http/Message/Header/HeaderFactoryInterface.php����S������Pvendor/guzzle/guzzle/src/Guzzle/Http/Message/EntityEnclosingRequestInterface.php}���S}�ٰ�Hvendor/guzzle/guzzle/src/Guzzle/Http/Message/RequestFactoryInterface.phpx���Sx�&۶Avendor/guzzle/guzzle/src/Guzzle/Http/Message/MessageInterface.php ���S ۓ`��Bvendor/guzzle/guzzle/src/Guzzle/Http/Message/PostFileInterface.php����S�F�J��9vendor/guzzle/guzzle/src/Guzzle/Http/Message/Response.php�c���S�c SѶ?vendor/guzzle/guzzle/src/Guzzle/Http/Message/RequestFactory.php�2���S�2d�I�9vendor/guzzle/guzzle/src/Guzzle/Http/Resources/cacert.pem�����S���\
31 �=vendor/guzzle/guzzle/src/Guzzle/Http/Resources/cacert.pem.md5!���S!�}�}�Lvendor/guzzle/guzzle/src/Guzzle/Http/Exception/TooManyRedirectsException.phpi���SiǸ��Ovendor/guzzle/guzzle/src/Guzzle/Http/Exception/ClientErrorResponseException.php����S�5"�ڶGvendor/guzzle/guzzle/src/Guzzle/Http/Exception/BadResponseException.phpQ���SQ3��?�Ovendor/guzzle/guzzle/src/Guzzle/Http/Exception/ServerErrorResponseException.php����S�Wp�@vendor/guzzle/guzzle/src/Guzzle/Http/Exception/HttpException.php����S�� �Cvendor/guzzle/guzzle/src/Guzzle/Http/Exception/RequestException.php���S�o~�Ivendor/guzzle/guzzle/src/Guzzle/Http/Exception/MultiTransferException.php� ���S� [ڒ��Pvendor/guzzle/guzzle/src/Guzzle/Http/Exception/CouldNotRewindStreamException.php����S��n��@vendor/guzzle/guzzle/src/Guzzle/Http/Exception/CurlException.phpC���SC&Y��3vendor/guzzle/guzzle/src/Guzzle/Http/EntityBody.php����S��á�<vendor/guzzle/guzzle/src/Guzzle/Http/EntityBodyInterface.phps ���Ss ����2vendor/guzzle/guzzle/src/Guzzle/Http/composer.json(���S(W�M�=vendor/guzzle/guzzle/src/Guzzle/Http/IoEmittingEntityBody.php����S��c�"�1vendor/guzzle/guzzle/src/Guzzle/Stream/Stream.php���S�
32 Ķ:vendor/guzzle/guzzle/src/Guzzle/Stream/StreamInterface.php8���S81���4vendor/guzzle/guzzle/src/Guzzle/Stream/composer.json����S���"U�Hvendor/guzzle/guzzle/src/Guzzle/Stream/StreamRequestFactoryInterface.php[���S[4�Bvendor/guzzle/guzzle/src/Guzzle/Stream/PhpStreamRequestFactory.php�#���S�#}��8vendor/guzzle/guzzle/src/Guzzle/Inflection/Inflector.phpS���SSB#c��Cvendor/guzzle/guzzle/src/Guzzle/Inflection/PreComputedInflector.php����S�\:��Avendor/guzzle/guzzle/src/Guzzle/Inflection/InflectorInterface.php>���S>ָ�k�Avendor/guzzle/guzzle/src/Guzzle/Inflection/MemoizingInflector.php����S�l�_t�8vendor/guzzle/guzzle/src/Guzzle/Inflection/composer.jsonr���SroR��-vendor/guzzle/guzzle/phing/imports/deploy.xml(���S(��ָ�.vendor/guzzle/guzzle/phing/imports/metrics.xml����S��[G�3vendor/guzzle/guzzle/phing/imports/dependencies.xml� ���S� ߢ�g�+vendor/guzzle/guzzle/phing/imports/test.xml����S�(��8�2vendor/guzzle/guzzle/phing/build.properties.travis����S��D0�0vendor/guzzle/guzzle/phing/build.properties.dist����S��U?6�>vendor/guzzle/guzzle/phing/tasks/GuzzlePearPharPackageTask.phpX/���SX/�i5vendor/guzzle/guzzle/phing/tasks/ComposerLintTask.php`���S`Wa�ζ7vendor/guzzle/guzzle/phing/tasks/GuzzleSubSplitTask.php�*���S�*�Ŷ3vendor/guzzle/guzzle/phing/tasks/NodeServerTask.php= ���S= ��F��vendor/guzzle/guzzle/build.xml4���S4��q�(vendor/guzzle/guzzle/tests/bootstrap.phpU���SU�e,�Cvendor/guzzle/guzzle/tests/Guzzle/Tests/TestData/test_service2.json]���S]�`�R�Evendor/guzzle/guzzle/tests/Guzzle/Tests/TestData/description/baz.json,���S,~�e3�Kvendor/guzzle/guzzle/tests/Guzzle/Tests/TestData/description/recursive.json ���S MR+�Evendor/guzzle/guzzle/tests/Guzzle/Tests/TestData/description/foo.json����S��<8��Evendor/guzzle/guzzle/tests/Guzzle/Tests/TestData/description/bar.json ���S MR+�Bvendor/guzzle/guzzle/tests/Guzzle/Tests/TestData/test_service.json����S�c�@�=vendor/guzzle/guzzle/tests/Guzzle/Tests/TestData/FileBody.txt���S�Dvendor/guzzle/guzzle/tests/Guzzle/Tests/TestData/services/json1.json����S����)�Gvendor/guzzle/guzzle/tests/Guzzle/Tests/TestData/services/services.json����S�8�sȶDvendor/guzzle/guzzle/tests/Guzzle/Tests/TestData/services/json2.json����S�:o�G�>vendor/guzzle/guzzle/tests/Guzzle/Tests/TestData/mock_response#���S#@}Q��Evendor/guzzle/guzzle/tests/Guzzle/Tests/Cache/Zf2CacheAdapterTest.php����S�C>*^�Ivendor/guzzle/guzzle/tests/Guzzle/Tests/Cache/CacheAdapterFactoryTest.php[���S[�H�1�Fvendor/guzzle/guzzle/tests/Guzzle/Tests/Cache/NullCacheAdapterTest.php���S���/�Bvendor/guzzle/guzzle/tests/Guzzle/Tests/Cache/CacheAdapterTest.php����S����Ivendor/guzzle/guzzle/tests/Guzzle/Tests/Cache/ClosureCacheAdapterTest.phpL
33 ���SL
34 ����Evendor/guzzle/guzzle/tests/Guzzle/Tests/Log/ClosureLogAdapterTest.php'���S'M�Cvendor/guzzle/guzzle/tests/Guzzle/Tests/Log/ArrayLogAdapterTest.php����S�T��:�Dvendor/guzzle/guzzle/tests/Guzzle/Tests/Log/MessageFormatterTest.phpd���SdB��Avendor/guzzle/guzzle/tests/Guzzle/Tests/Log/Zf2LogAdapterTest.php#���S#��虶Avendor/guzzle/guzzle/tests/Guzzle/Tests/Log/PsrLogAdapterTest.php����S�R���Gvendor/guzzle/guzzle/tests/Guzzle/Tests/Iterator/AppendIteratorTest.php����S�y �C�Gvendor/guzzle/guzzle/tests/Guzzle/Tests/Iterator/FilterIteratorTest.php����S�ڄX�Hvendor/guzzle/guzzle/tests/Guzzle/Tests/Iterator/ChunkedIteratorTest.php���S����Lvendor/guzzle/guzzle/tests/Guzzle/Tests/Iterator/MethodProxyIteratorTest.phpk���Sk���Dvendor/guzzle/guzzle/tests/Guzzle/Tests/Iterator/MapIteratorTest.php~���S~n���Nvendor/guzzle/guzzle/tests/Guzzle/Tests/Plugin/CurlAuth/CurlAuthPluginTest.php����S�b^c �Hvendor/guzzle/guzzle/tests/Guzzle/Tests/Plugin/Async/AsyncPluginTest.php� ���S� j�1H�Rvendor/guzzle/guzzle/tests/Guzzle/Tests/Plugin/Md5/CommandContentMd5PluginTest.php� ���S� �P蛶Mvendor/guzzle/guzzle/tests/Guzzle/Tests/Plugin/Md5/Md5ValidatorPluginTest.php����S�KI�ŶPvendor/guzzle/guzzle/tests/Guzzle/Tests/Plugin/Cache/DefaultCacheStorageTest.phpC���SCM�i�Hvendor/guzzle/guzzle/tests/Guzzle/Tests/Plugin/Cache/CachePluginTest.php Q���S QMR�+�Mvendor/guzzle/guzzle/tests/Guzzle/Tests/Plugin/Cache/SkipRevalidationTest.php����S�mr�¶Uvendor/guzzle/guzzle/tests/Guzzle/Tests/Plugin/Cache/CallbackCanCacheStrategyTest.php� ���S� &̛P�Tvendor/guzzle/guzzle/tests/Guzzle/Tests/Plugin/Cache/DefaultCanCacheStrategyTest.php���Sh����Mvendor/guzzle/guzzle/tests/Guzzle/Tests/Plugin/Cache/DenyRevalidationTest.php����S�H�N��Pvendor/guzzle/guzzle/tests/Guzzle/Tests/Plugin/Cache/DefaultRevalidationTest.php4!���S4!s��Dvendor/guzzle/guzzle/tests/Guzzle/Tests/Plugin/Log/LogPluginTest.phpt ���St �:��Lvendor/guzzle/guzzle/tests/Guzzle/Tests/Plugin/Backoff/BackoffPluginTest.php�-���S�-�>��Zvendor/guzzle/guzzle/tests/Guzzle/Tests/Plugin/Backoff/ReasonPhraseBackoffStrategyTest.php����S�1�)��Tvendor/guzzle/guzzle/tests/Guzzle/Tests/Plugin/Backoff/LinearBackoffStrategyTest.php����S�S$&��Lvendor/guzzle/guzzle/tests/Guzzle/Tests/Plugin/Backoff/BackoffLoggerTest.php� ���S� >�4v�Vvendor/guzzle/guzzle/tests/Guzzle/Tests/Plugin/Backoff/CallbackBackoffStrategyTest.phpI���SI�� �Vvendor/guzzle/guzzle/tests/Guzzle/Tests/Plugin/Backoff/ConstantBackoffStrategyTest.php����S�! �?�Rvendor/guzzle/guzzle/tests/Guzzle/Tests/Plugin/Backoff/HttpBackoffStrategyTest.php2���S2''zW�Yvendor/guzzle/guzzle/tests/Guzzle/Tests/Plugin/Backoff/ExponentialBackoffStrategyTest.phpz���Sz��^�Rvendor/guzzle/guzzle/tests/Guzzle/Tests/Plugin/Backoff/CurlBackoffStrategyTest.phpb���Sb\q���Vvendor/guzzle/guzzle/tests/Guzzle/Tests/Plugin/Backoff/AbstractBackoffStrategyTest.php" ���S" 0��˶Wvendor/guzzle/guzzle/tests/Guzzle/Tests/Plugin/Backoff/TruncatedBackoffStrategyTest.php����S�p��5�Hvendor/guzzle/guzzle/tests/Guzzle/Tests/Plugin/Oauth/OauthPluginTest.phpO'���SO'��S�Uvendor/guzzle/guzzle/tests/Guzzle/Tests/Plugin/Cookie/CookieJar/FileCookieJarTest.phpi���Si(0�߶Vvendor/guzzle/guzzle/tests/Guzzle/Tests/Plugin/Cookie/CookieJar/ArrayCookieJarTest.phpf1���Sf1�u��Jvendor/guzzle/guzzle/tests/Guzzle/Tests/Plugin/Cookie/CookiePluginTest.php;���S;�{��Dvendor/guzzle/guzzle/tests/Guzzle/Tests/Plugin/Cookie/CookieTest.php���S��l��Fvendor/guzzle/guzzle/tests/Guzzle/Tests/Plugin/Mock/MockPluginTest.php����S���m��Xvendor/guzzle/guzzle/tests/Guzzle/Tests/Plugin/ErrorResponse/ErrorResponsePluginTest.php����S�Z>�Lvendor/guzzle/guzzle/tests/Guzzle/Tests/Plugin/History/HistoryPluginTest.phpa���Sa�����Avendor/guzzle/guzzle/tests/Guzzle/Tests/Common/CollectionTest.php�C���S�CP崶<vendor/guzzle/guzzle/tests/Guzzle/Tests/Common/EventTest.php����S�=W)ʶLvendor/guzzle/guzzle/tests/Guzzle/Tests/Common/AbstractHasDispatcherTest.php[���S[:�N�Wvendor/guzzle/guzzle/tests/Guzzle/Tests/Common/Exception/BatchTransferExceptionTest.phpd���Sd���7�Tvendor/guzzle/guzzle/tests/Guzzle/Tests/Common/Exception/ExceptionCollectionTest.php����S��.n�>vendor/guzzle/guzzle/tests/Guzzle/Tests/Common/VersionTest.php���S�r�նKvendor/guzzle/guzzle/tests/Guzzle/Tests/Service/CachingConfigLoaderTest.php����S��N�Svendor/guzzle/guzzle/tests/Guzzle/Tests/Service/Description/SchemaFormatterTest.phpC���SC�� ��Mvendor/guzzle/guzzle/tests/Guzzle/Tests/Service/Description/ParameterTest.phpl6���Sl6k��J�\vendor/guzzle/guzzle/tests/Guzzle/Tests/Service/Description/ServiceDescriptionLoaderTest.php����S��c��Mvendor/guzzle/guzzle/tests/Guzzle/Tests/Service/Description/OperationTest.php�,���S�,�/ ҶVvendor/guzzle/guzzle/tests/Guzzle/Tests/Service/Description/ServiceDescriptionTest.php !���S !7� �Svendor/guzzle/guzzle/tests/Guzzle/Tests/Service/Description/SchemaValidatorTest.php�0���S�0�!��Lvendor/guzzle/guzzle/tests/Guzzle/Tests/Service/AbstractConfigLoaderTest.php����S�� �U�Fvendor/guzzle/guzzle/tests/Guzzle/Tests/Service/Resource/ModelTest.php����S�K�#L�Qvendor/guzzle/guzzle/tests/Guzzle/Tests/Service/Resource/ResourceIteratorTest.php���Sk4��]vendor/guzzle/guzzle/tests/Guzzle/Tests/Service/Resource/ResourceIteratorClassFactoryTest.php����S���pӶavendor/guzzle/guzzle/tests/Guzzle/Tests/Service/Resource/CompositeResourceIteratorFactoryTest.php8���S8�摶[vendor/guzzle/guzzle/tests/Guzzle/Tests/Service/Resource/MapResourceIteratorFactoryTest.phpJ���SJj�%�evendor/guzzle/guzzle/tests/Guzzle/Tests/Service/Exception/InconsistentClientTransferExceptionTest.php����S�\f �Uvendor/guzzle/guzzle/tests/Guzzle/Tests/Service/Exception/ValidationExceptionTest.php����S�8-a��Zvendor/guzzle/guzzle/tests/Guzzle/Tests/Service/Exception/CommandTransferExceptionTest.php
35 ���S
36 05�ݶ>vendor/guzzle/guzzle/tests/Guzzle/Tests/Service/ClientTest.php�)���S�)<<��Tvendor/guzzle/guzzle/tests/Guzzle/Tests/Service/Builder/ServiceBuilderLoaderTest.php����S�ؤ ��Nvendor/guzzle/guzzle/tests/Guzzle/Tests/Service/Builder/ServiceBuilderTest.phpk+���Sk+a:퍶Cvendor/guzzle/guzzle/tests/Guzzle/Tests/Service/Mock/MockClient.php����S��c��Rvendor/guzzle/guzzle/tests/Guzzle/Tests/Service/Mock/Model/MockCommandIterator.phpp���Sp�b��Hvendor/guzzle/guzzle/tests/Guzzle/Tests/Service/Mock/Command/Sub/Sub.php����S��c٥�Lvendor/guzzle/guzzle/tests/Guzzle/Tests/Service/Mock/Command/MockCommand.phpu���Su�1���Mvendor/guzzle/guzzle/tests/Guzzle/Tests/Service/Mock/Command/OtherCommand.php���SŲ|��Pvendor/guzzle/guzzle/tests/Guzzle/Tests/Service/Mock/Command/IterableCommand.phpJ���SJ%% �Rvendor/guzzle/guzzle/tests/Guzzle/Tests/Service/Command/Factory/MapFactoryTest.php����S�ϸ�&�\vendor/guzzle/guzzle/tests/Guzzle/Tests/Service/Command/Factory/ConcreteClassFactoryTest.php����S�b�ͶTvendor/guzzle/guzzle/tests/Guzzle/Tests/Service/Command/Factory/AliasFactoryTest.phpc ���Sc Nv8�avendor/guzzle/guzzle/tests/Guzzle/Tests/Service/Command/Factory/ServiceDescriptionFactoryTest.php
37 ���S
38 ����Xvendor/guzzle/guzzle/tests/Guzzle/Tests/Service/Command/Factory/CompositeFactoryTest.php����S�� �[�Uvendor/guzzle/guzzle/tests/Guzzle/Tests/Service/Command/DefaultResponseParserTest.php����S��q�Wvendor/guzzle/guzzle/tests/Guzzle/Tests/Service/Command/OperationResponseParserTest.php�2���S�2�� ��Gvendor/guzzle/guzzle/tests/Guzzle/Tests/Service/Command/CommandTest.php�7���S�7��Nvendor/guzzle/guzzle/tests/Guzzle/Tests/Service/Command/ClosureCommandTest.php3���S35�ߧ�Pvendor/guzzle/guzzle/tests/Guzzle/Tests/Service/Command/OperationCommandTest.php5���S5Ly+<�Ovendor/guzzle/guzzle/tests/Guzzle/Tests/Service/Command/AbstractCommandTest.php����S���U!�evendor/guzzle/guzzle/tests/Guzzle/Tests/Service/Command/LocationVisitor/Request/HeaderVisitorTest.php����S���RU�gvendor/guzzle/guzzle/tests/Guzzle/Tests/Service/Command/LocationVisitor/Request/PostFileVisitorTest.php,���S,��f�cvendor/guzzle/guzzle/tests/Guzzle/Tests/Service/Command/LocationVisitor/Request/BodyVisitorTest.php� ���S� "�+T�cvendor/guzzle/guzzle/tests/Guzzle/Tests/Service/Command/LocationVisitor/Request/JsonVisitorTest.phpf ���Sf 0`�dvendor/guzzle/guzzle/tests/Guzzle/Tests/Service/Command/LocationVisitor/Request/QueryVisitorTest.php����S���࠶bvendor/guzzle/guzzle/tests/Guzzle/Tests/Service/Command/LocationVisitor/Request/XmlVisitorTest.php�T���S�Ty�ρ�kvendor/guzzle/guzzle/tests/Guzzle/Tests/Service/Command/LocationVisitor/Request/AbstractVisitorTestCase.php����S��H�׶kvendor/guzzle/guzzle/tests/Guzzle/Tests/Service/Command/LocationVisitor/Request/ResponseBodyVisitorTest.php����S�o0c$�hvendor/guzzle/guzzle/tests/Guzzle/Tests/Service/Command/LocationVisitor/Request/PostFieldVisitorTest.php����S��Vl�fvendor/guzzle/guzzle/tests/Guzzle/Tests/Service/Command/LocationVisitor/Response/HeaderVisitorTest.php+ ���S+ ���ƶpvendor/guzzle/guzzle/tests/Guzzle/Tests/Service/Command/LocationVisitor/Response/AbstractResponseVisitorTest.php����S���O��dvendor/guzzle/guzzle/tests/Guzzle/Tests/Service/Command/LocationVisitor/Response/BodyVisitorTest.php����S��r��dvendor/guzzle/guzzle/tests/Guzzle/Tests/Service/Command/LocationVisitor/Response/JsonVisitorTest.php����S���ۜ�cvendor/guzzle/guzzle/tests/Guzzle/Tests/Service/Command/LocationVisitor/Response/XmlVisitorTest.php3:���S3:�*�O�jvendor/guzzle/guzzle/tests/Guzzle/Tests/Service/Command/LocationVisitor/Response/StatusCodeVisitorTest.php����S�&^�۶lvendor/guzzle/guzzle/tests/Guzzle/Tests/Service/Command/LocationVisitor/Response/ReasonPhraseVisitorTest.php����S���Ҟ�`vendor/guzzle/guzzle/tests/Guzzle/Tests/Service/Command/LocationVisitor/VisitorFlyweightTest.phpK���SK�"��Xvendor/guzzle/guzzle/tests/Guzzle/Tests/Service/Command/DefaultRequestSerializerTest.php����S��٢�Dvendor/guzzle/guzzle/tests/Guzzle/Tests/Batch/NotifyingBatchTest.php���S���ѶFvendor/guzzle/guzzle/tests/Guzzle/Tests/Batch/BatchSizeDivisorTest.phpN���SN�8�9�Jvendor/guzzle/guzzle/tests/Guzzle/Tests/Batch/BatchCommandTransferTest.php� ���S� un �Cvendor/guzzle/guzzle/tests/Guzzle/Tests/Batch/FlushingBatchTest.php����S��3rʶ;vendor/guzzle/guzzle/tests/Guzzle/Tests/Batch/BatchTest.php ���S �b.ǶJvendor/guzzle/guzzle/tests/Guzzle/Tests/Batch/BatchRequestTransferTest.phpr ���Sr E �Lvendor/guzzle/guzzle/tests/Guzzle/Tests/Batch/AbstractBatchDecoratorTest.php$���S$|R�ͶIvendor/guzzle/guzzle/tests/Guzzle/Tests/Batch/BatchClosureDivisorTest.phpc���Sc�жBvendor/guzzle/guzzle/tests/Guzzle/Tests/Batch/BatchBuilderTest.php!
39 ���S!
40 �} `�Bvendor/guzzle/guzzle/tests/Guzzle/Tests/Batch/HistoryBatchTest.php����S���FJ�Jvendor/guzzle/guzzle/tests/Guzzle/Tests/Batch/BatchClosureTransferTest.php����S���" �Mvendor/guzzle/guzzle/tests/Guzzle/Tests/Batch/ExceptionBufferingBatchTest.php9���S9�9�Evendor/guzzle/guzzle/tests/Guzzle/Tests/Parser/ParserRegistryTest.php+���S+�E���Rvendor/guzzle/guzzle/tests/Guzzle/Tests/Parser/UriTemplate/PeclUriTemplateTest.php����S�����Nvendor/guzzle/guzzle/tests/Guzzle/Tests/Parser/UriTemplate/UriTemplateTest.php� ���S� ��U#�Vvendor/guzzle/guzzle/tests/Guzzle/Tests/Parser/UriTemplate/AbstractUriTemplateTest.phpZ���SZ�E�x�Jvendor/guzzle/guzzle/tests/Guzzle/Tests/Parser/Cookie/CookieParserTest.php���SY|�жNvendor/guzzle/guzzle/tests/Guzzle/Tests/Parser/Cookie/CookieParserProvider.php,���S,���ӶLvendor/guzzle/guzzle/tests/Guzzle/Tests/Parser/Message/MessageParserTest.php>���S>'��Pvendor/guzzle/guzzle/tests/Guzzle/Tests/Parser/Message/MessageParserProvider.phpB ���SB �_�նTvendor/guzzle/guzzle/tests/Guzzle/Tests/Parser/Message/PeclHttpMessageParserTest.php����S�@K��:vendor/guzzle/guzzle/tests/Guzzle/Tests/GuzzleTestCase.php����S�����7vendor/guzzle/guzzle/tests/Guzzle/Tests/Http/Server.php����S��۰C�Dvendor/guzzle/guzzle/tests/Guzzle/Tests/Http/Curl/CurlHandleTest.php�����S��~fh �Hvendor/guzzle/guzzle/tests/Guzzle/Tests/Http/Curl/CurlMultiProxyTest.php\ ���S\ !jH�Ivendor/guzzle/guzzle/tests/Guzzle/Tests/Http/Curl/RequestMediatorTest.php
41 ���S
42 -��c�Evendor/guzzle/guzzle/tests/Guzzle/Tests/Http/Curl/CurlVersionTest.php����S�
43 ���Cvendor/guzzle/guzzle/tests/Guzzle/Tests/Http/Curl/CurlMultiTest.php�D���S�D��2 �Tvendor/guzzle/guzzle/tests/Guzzle/Tests/Http/QueryAggregator/CommaAggregatorTest.php`���S`�Ql{�Rvendor/guzzle/guzzle/tests/Guzzle/Tests/Http/QueryAggregator/PhpAggregatorTest.php����S�i��Xvendor/guzzle/guzzle/tests/Guzzle/Tests/Http/QueryAggregator/DuplicateAggregatorTest.phpp���SpM���Ivendor/guzzle/guzzle/tests/Guzzle/Tests/Http/IoEmittingEntityBodyTest.phpb���Sbk+�Cvendor/guzzle/guzzle/tests/Guzzle/Tests/Http/RedirectPluginTest.php�'���S�'|�5�Hvendor/guzzle/guzzle/tests/Guzzle/Tests/Http/ReadLimitEntityBodyTest.php����S��ƿ|�Fvendor/guzzle/guzzle/tests/Guzzle/Tests/Http/CachingEntityBodyTest.php#���S#�8��Avendor/guzzle/guzzle/tests/Guzzle/Tests/Http/StaticClientTest.php! ���S! HbYǶ6vendor/guzzle/guzzle/tests/Guzzle/Tests/Http/server.jsd���SdS`;1�Pvendor/guzzle/guzzle/tests/Guzzle/Tests/Http/AbstractEntityBodyDecoratorTest.php����S���N8�Kvendor/guzzle/guzzle/tests/Guzzle/Tests/Http/Message/RequestFactoryTest.php�f���S�f��Њ�Lvendor/guzzle/guzzle/tests/Guzzle/Tests/Http/Message/AbstractMessageTest.php����S���w�Evendor/guzzle/guzzle/tests/Guzzle/Tests/Http/Message/PostFileTest.php� ���S� �=���Mvendor/guzzle/guzzle/tests/Guzzle/Tests/Http/Message/HeaderComparisonTest.phpn ���Sn ևo��Hvendor/guzzle/guzzle/tests/Guzzle/Tests/Http/Message/Header/LinkTest.php���S|-,:�Qvendor/guzzle/guzzle/tests/Guzzle/Tests/Http/Message/Header/HeaderFactoryTest.phpi���Si#�M�Svendor/guzzle/guzzle/tests/Guzzle/Tests/Http/Message/EntityEnclosingRequestTest.php�@���S�@��n�Cvendor/guzzle/guzzle/tests/Guzzle/Tests/Http/Message/HeaderTest.php����S���Y�Ivendor/guzzle/guzzle/tests/Guzzle/Tests/Http/Message/HeaderComparison.php����S��ꢧ�Dvendor/guzzle/guzzle/tests/Guzzle/Tests/Http/Message/RequestTest.php�c���S�c�r��Evendor/guzzle/guzzle/tests/Guzzle/Tests/Http/Message/ResponseTest.php[[���S[[D4ZC�@vendor/guzzle/guzzle/tests/Guzzle/Tests/Http/QueryStringTest.php����S�K��v�Lvendor/guzzle/guzzle/tests/Guzzle/Tests/Http/Exception/CurlExceptionTest.php����S��q��Uvendor/guzzle/guzzle/tests/Guzzle/Tests/Http/Exception/MultiTransferExceptionTest.phpr���Sr��1��Hvendor/guzzle/guzzle/tests/Guzzle/Tests/Http/Exception/ExceptionTest.php����S��>lJ�;vendor/guzzle/guzzle/tests/Guzzle/Tests/Http/ClientTest.php�X���S�X��k�8vendor/guzzle/guzzle/tests/Guzzle/Tests/Http/UrlTest.php$���S$aO���>vendor/guzzle/guzzle/tests/Guzzle/Tests/Http/MimetypesTest.php���S��HF�?vendor/guzzle/guzzle/tests/Guzzle/Tests/Http/EntityBodyTest.php����S���ж:vendor/guzzle/guzzle/tests/Guzzle/Tests/Mock/MockMulti.php����S�׵��Dvendor/guzzle/guzzle/tests/Guzzle/Tests/Mock/CustomResponseModel.php����S�/%��=vendor/guzzle/guzzle/tests/Guzzle/Tests/Mock/MockObserver.phpC���SC�ǖw�>vendor/guzzle/guzzle/tests/Guzzle/Tests/Mock/ExceptionMock.php����S����,�<vendor/guzzle/guzzle/tests/Guzzle/Tests/Mock/MockSubject.phph���Shsq�ǶBvendor/guzzle/guzzle/tests/Guzzle/Tests/Mock/ErrorResponseMock.php����S���f�Nvendor/guzzle/guzzle/tests/Guzzle/Tests/Stream/PhpStreamRequestFactoryTest.php����S�$��=vendor/guzzle/guzzle/tests/Guzzle/Tests/Stream/StreamTest.php����S��u��Ovendor/guzzle/guzzle/tests/Guzzle/Tests/Inflection/PreComputedInflectorTest.php���S����Mvendor/guzzle/guzzle/tests/Guzzle/Tests/Inflection/MemoizingInflectorTest.php����S����Dvendor/guzzle/guzzle/tests/Guzzle/Tests/Inflection/InflectorTest.php����S����!vendor/guzzle/guzzle/CHANGELOG.md�����S��b��C�"vendor/guzzle/guzzle/composer.json����S��m�!�%vendor/guzzle/guzzle/phpunit.xml.distJ ���SJ ��J�!vendor/guzzle/guzzle/UPGRADING.mdT���STŗ��,vendor/phpcollection/phpcollection/README.mdr���Sr�'��*vendor/phpcollection/phpcollection/LICENSE\,���S\,�bE�Kvendor/phpcollection/phpcollection/src/PhpCollection/AbstractCollection.php=���S=���O�<vendor/phpcollection/phpcollection/src/PhpCollection/Map.php2���S2#b��Lvendor/phpcollection/phpcollection/src/PhpCollection/CollectionInterface.php����S����Jvendor/phpcollection/phpcollection/src/PhpCollection/SequenceInterface.phpd���Sd�����Dvendor/phpcollection/phpcollection/src/PhpCollection/AbstractMap.phpU���SUȰ�Ivendor/phpcollection/phpcollection/src/PhpCollection/AbstractSequence.php����S��R׶Avendor/phpcollection/phpcollection/src/PhpCollection/Sequence.php^���S^iU���Jvendor/phpcollection/phpcollection/src/PhpCollection/SortableInterface.phpD���SDhK-J�Evendor/phpcollection/phpcollection/src/PhpCollection/MapInterface.phpv���SvXZWg�Gvendor/phpcollection/phpcollection/src/PhpCollection/SortedSequence.php ���S �=?�0vendor/phpcollection/phpcollection/composer.lock���S1�(޶.vendor/phpcollection/phpcollection/doc/LICENSE?���S?F�D�0vendor/phpcollection/phpcollection/doc/index.rst� ���S� t��*�6vendor/phpcollection/phpcollection/tests/bootstrap.php����S��?�Mvendor/phpcollection/phpcollection/tests/PhpCollection/Tests/SequenceTest.php����S�����Svendor/phpcollection/phpcollection/tests/PhpCollection/Tests/SortedSequenceTest.php����S�z+ J�Hvendor/phpcollection/phpcollection/tests/PhpCollection/Tests/MapTest.php����S���p��0vendor/phpcollection/phpcollection/composer.json ���S ��r �3vendor/phpcollection/phpcollection/phpunit.xml.dist����S���x��!vendor/composer/autoload_real.phpn���SnOc�vendor/composer/installed.json�V���S�Va��'vendor/composer/autoload_namespaces.php����S�0f���!vendor/composer/autoload_psr4.php����S����D�%vendor/composer/autoload_classmap.php����S���b�vendor/composer/ClassLoader.php+���S+��t�Ovendor/symfony/console/Symfony/Component/Console/Tests/Helper/HelperSetTest.phpV���SVйnY�Uvendor/symfony/console/Symfony/Component/Console/Tests/Helper/FormatterHelperTest.php�
44 ���S�
45 gO��Tvendor/symfony/console/Symfony/Component/Console/Tests/Helper/ProgressHelperTest.php1���S1A�D�Rvendor/symfony/console/Symfony/Component/Console/Tests/Helper/DialogHelperTest.phpT!���ST!uB[��Qvendor/symfony/console/Symfony/Component/Console/Tests/Helper/TableHelperTest.php����S���L�Xvendor/symfony/console/Symfony/Component/Console/Tests/Formatter/OutputFormatterTest.php����S�M��]vendor/symfony/console/Symfony/Component/Console/Tests/Formatter/OutputFormatterStyleTest.phpw���Sw�r�H�bvendor/symfony/console/Symfony/Component/Console/Tests/Formatter/OutputFormatterStyleStackTest.phpv���Sv��¶Rvendor/symfony/console/Symfony/Component/Console/Tests/Output/StreamOutputTest.php���S�����Lvendor/symfony/console/Symfony/Component/Console/Tests/Output/OutputTest.php���Sw}Xa�Svendor/symfony/console/Symfony/Component/Console/Tests/Output/ConsoleOutputTest.php����S�}�R�Pvendor/symfony/console/Symfony/Component/Console/Tests/Output/NullOutputTest.php����S�;�! �Tvendor/symfony/console/Symfony/Component/Console/Tests/Fixtures/application_run2.txt���S6u�Rvendor/symfony/console/Symfony/Component/Console/Tests/Fixtures/input_option_4.xml����S��IƶOvendor/symfony/console/Symfony/Component/Console/Tests/Fixtures/TestCommand.php����S�9�*A�Rvendor/symfony/console/Symfony/Component/Console/Tests/Fixtures/input_option_4.txta���Sa�t;��Lvendor/symfony/console/Symfony/Component/Console/Tests/Fixtures/command_2.md����S���U϶Wvendor/symfony/console/Symfony/Component/Console/Tests/Fixtures/input_definition_2.json����S�����Tvendor/symfony/console/Symfony/Component/Console/Tests/Fixtures/input_argument_1.txt���S(?h�Vvendor/symfony/console/Symfony/Component/Console/Tests/Fixtures/input_definition_4.xml����S�CQu�Ovendor/symfony/console/Symfony/Component/Console/Tests/Fixtures/Foo4Command.php����S��e�d�Zvendor/symfony/console/Symfony/Component/Console/Tests/Fixtures/DescriptorApplication2.phpM���SM1"8�Wvendor/symfony/console/Symfony/Component/Console/Tests/Fixtures/application_gethelp.txt����S�#ɤb�Uvendor/symfony/console/Symfony/Component/Console/Tests/Fixtures/input_definition_3.md����S�p����Vvendor/symfony/console/Symfony/Component/Console/Tests/Fixtures/input_definition_3.txt=���S=����Svendor/symfony/console/Symfony/Component/Console/Tests/Fixtures/input_option_2.json����S�۴�2�Ovendor/symfony/console/Symfony/Component/Console/Tests/Fixtures/Foo1Command.phpU���SU<|�4�Mvendor/symfony/console/Symfony/Component/Console/Tests/Fixtures/command_2.txt����S�h���Tvendor/symfony/console/Symfony/Component/Console/Tests/Fixtures/definition_asxml.txt����S�ia��Rvendor/symfony/console/Symfony/Component/Console/Tests/Fixtures/application_2.json����S�m��Wvendor/symfony/console/Symfony/Component/Console/Tests/Fixtures/application_astext1.txt����S�5�5�Svendor/symfony/console/Symfony/Component/Console/Tests/Fixtures/input_argument_1.mdt���St���Svendor/symfony/console/Symfony/Component/Console/Tests/Fixtures/input_argument_2.md����S�z�Ռ�Nvendor/symfony/console/Symfony/Component/Console/Tests/Fixtures/command_2.json����S�#��"�Vvendor/symfony/console/Symfony/Component/Console/Tests/Fixtures/input_definition_3.xml���S�F:c�Ovendor/symfony/console/Symfony/Component/Console/Tests/Fixtures/Foo2Command.php����S�� �Qvendor/symfony/console/Symfony/Component/Console/Tests/Fixtures/input_option_3.md����S���c�Rvendor/symfony/console/Symfony/Component/Console/Tests/Fixtures/input_option_1.txt!���S!u$´�Uvendor/symfony/console/Symfony/Component/Console/Tests/Fixtures/input_argument_3.json}���S}󚲱�Rvendor/symfony/console/Symfony/Component/Console/Tests/Fixtures/input_option_2.txtb���Sb�K@�Vvendor/symfony/console/Symfony/Component/Console/Tests/Fixtures/input_definition_1.xml^���S^�^tݶ`vendor/symfony/console/Symfony/Component/Console/Tests/Fixtures/application_renderexception2.txt����S�,DŽ�Mvendor/symfony/console/Symfony/Component/Console/Tests/Fixtures/command_2.xmlf���Sf�A�(�Nvendor/symfony/console/Symfony/Component/Console/Tests/Fixtures/command_1.json����S�[��%�Rvendor/symfony/console/Symfony/Component/Console/Tests/Fixtures/application_1.json����S���0�Uvendor/symfony/console/Symfony/Component/Console/Tests/Fixtures/input_argument_1.json]���S]��<�Uvendor/symfony/console/Symfony/Component/Console/Tests/Fixtures/input_definition_1.md���S�Svendor/symfony/console/Symfony/Component/Console/Tests/Fixtures/input_option_4.json����S� a��Qvendor/symfony/console/Symfony/Component/Console/Tests/Fixtures/application_2.xml�!���S�!�4~��Svendor/symfony/console/Symfony/Component/Console/Tests/Fixtures/input_argument_3.md����S�W��ض`vendor/symfony/console/Symfony/Component/Console/Tests/Fixtures/application_renderexception3.txt����S�� 緶Tvendor/symfony/console/Symfony/Component/Console/Tests/Fixtures/input_argument_3.xml����S��: 8�Uvendor/symfony/console/Symfony/Component/Console/Tests/Fixtures/input_argument_2.jsono���So����Tvendor/symfony/console/Symfony/Component/Console/Tests/Fixtures/input_argument_2.txt1���S1g��/�Vvendor/symfony/console/Symfony/Component/Console/Tests/Fixtures/application_asxml2.txt����S�W'�Tvendor/symfony/console/Symfony/Component/Console/Tests/Fixtures/input_argument_3.txt_���S_�NY�Uvendor/symfony/console/Symfony/Component/Console/Tests/Fixtures/input_definition_4.md4���S4*[Ǿ�Tvendor/symfony/console/Symfony/Component/Console/Tests/Fixtures/input_argument_1.xml����S��_�n�Vvendor/symfony/console/Symfony/Component/Console/Tests/Fixtures/application_asxml1.txt����S���¶Pvendor/symfony/console/Symfony/Component/Console/Tests/Fixtures/application_1.md����S��F�3�Tvendor/symfony/console/Symfony/Component/Console/Tests/Fixtures/application_run3.txt����S��U���Wvendor/symfony/console/Symfony/Component/Console/Tests/Fixtures/input_definition_4.json$���S$���Vvendor/symfony/console/Symfony/Component/Console/Tests/Fixtures/DescriptorCommand1.php����S�Y'�A�`vendor/symfony/console/Symfony/Component/Console/Tests/Fixtures/application_renderexception4.txt����S��Xl�Vvendor/symfony/console/Symfony/Component/Console/Tests/Fixtures/input_definition_2.xml����S�3�P߶Tvendor/symfony/console/Symfony/Component/Console/Tests/Fixtures/application_run4.txt ���S 8�UT�Zvendor/symfony/console/Symfony/Component/Console/Tests/Fixtures/DescriptorApplication1.php����S��q+�Wvendor/symfony/console/Symfony/Component/Console/Tests/Fixtures/input_definition_3.json����S�*�q�Qvendor/symfony/console/Symfony/Component/Console/Tests/Fixtures/input_option_1.md����S��Y-ͶQvendor/symfony/console/Symfony/Component/Console/Tests/Fixtures/application_2.txt����S��(��Uvendor/symfony/console/Symfony/Component/Console/Tests/Fixtures/definition_astext.txt����S���,ݶ`vendor/symfony/console/Symfony/Component/Console/Tests/Fixtures/application_renderexception1.txt����S�&��Qvendor/symfony/console/Symfony/Component/Console/Tests/Fixtures/input_option_4.md����S�P���Rvendor/symfony/console/Symfony/Component/Console/Tests/Fixtures/input_option_3.txt4���S4��x��Svendor/symfony/console/Symfony/Component/Console/Tests/Fixtures/input_option_3.json����S���I�Tvendor/symfony/console/Symfony/Component/Console/Tests/Fixtures/input_argument_2.xml����S���f��Rvendor/symfony/console/Symfony/Component/Console/Tests/Fixtures/input_option_3.xml����S���XX�Tvendor/symfony/console/Symfony/Component/Console/Tests/Fixtures/application_run1.txt:���S:�e�Vvendor/symfony/console/Symfony/Component/Console/Tests/Fixtures/input_definition_4.txt~���S~.p�Rvendor/symfony/console/Symfony/Component/Console/Tests/Fixtures/input_option_2.xml���S�J�2�Qvendor/symfony/console/Symfony/Component/Console/Tests/Fixtures/application_1.txt6���S6��a�Rvendor/symfony/console/Symfony/Component/Console/Tests/Fixtures/command_astext.txt����S���׶Mvendor/symfony/console/Symfony/Component/Console/Tests/Fixtures/command_1.xmlO���SO��E�Uvendor/symfony/console/Symfony/Component/Console/Tests/Fixtures/input_definition_2.md����S���[��Vvendor/symfony/console/Symfony/Component/Console/Tests/Fixtures/DescriptorCommand2.php?���S?XzԶVvendor/symfony/console/Symfony/Component/Console/Tests/Fixtures/input_definition_1.txt���S�Qvendor/symfony/console/Symfony/Component/Console/Tests/Fixtures/input_option_2.md����S� �ӶVvendor/symfony/console/Symfony/Component/Console/Tests/Fixtures/input_definition_2.txt;���S;Ѹ�Wvendor/symfony/console/Symfony/Component/Console/Tests/Fixtures/input_definition_1.json���S�����Rvendor/symfony/console/Symfony/Component/Console/Tests/Fixtures/input_option_1.xml����S���G�Qvendor/symfony/console/Symfony/Component/Console/Tests/Fixtures/application_1.xml4���S4�P��Ovendor/symfony/console/Symfony/Component/Console/Tests/Fixtures/Foo3Command.phpm���Sm�����Wvendor/symfony/console/Symfony/Component/Console/Tests/Fixtures/application_astext2.txt#���S#(�K�Qvendor/symfony/console/Symfony/Component/Console/Tests/Fixtures/command_asxml.txt����S��S�Nvendor/symfony/console/Symfony/Component/Console/Tests/Fixtures/FooCommand.php���S� c��Lvendor/symfony/console/Symfony/Component/Console/Tests/Fixtures/command_1.md����S�9y�X�Pvendor/symfony/console/Symfony/Component/Console/Tests/Fixtures/application_2.md����S�76��Svendor/symfony/console/Symfony/Component/Console/Tests/Fixtures/input_option_1.json����S���X�Mvendor/symfony/console/Symfony/Component/Console/Tests/Fixtures/command_1.txt����S��'�̶Jvendor/symfony/console/Symfony/Component/Console/Tests/ApplicationTest.phpљ���Sљ� 0�\vendor/symfony/console/Symfony/Component/Console/Tests/Descriptor/MarkdownDescriptorTest.phpD���SD1H�D�Wvendor/symfony/console/Symfony/Component/Console/Tests/Descriptor/XmlDescriptorTest.php6���S6��₶Xvendor/symfony/console/Symfony/Component/Console/Tests/Descriptor/TextDescriptorTest.php9���S9K� �\vendor/symfony/console/Symfony/Component/Console/Tests/Descriptor/AbstractDescriptorTest.php� ���S� l�Vt�Xvendor/symfony/console/Symfony/Component/Console/Tests/Descriptor/JsonDescriptorTest.php:���S:oW��Uvendor/symfony/console/Symfony/Component/Console/Tests/Descriptor/ObjectsProvider.phpJ ���SJ �ž_�Rvendor/symfony/console/Symfony/Component/Console/Tests/Command/ListCommandTest.phpC ���SC �˶Nvendor/symfony/console/Symfony/Component/Console/Tests/Command/CommandTest.phpZ9���SZ9o�"��Rvendor/symfony/console/Symfony/Component/Console/Tests/Command/HelpCommandTest.php1 ���S1 b�yζSvendor/symfony/console/Symfony/Component/Console/Tests/Tester/CommandTesterTest.php����S��n�}�Wvendor/symfony/console/Symfony/Component/Console/Tests/Tester/ApplicationTesterTest.php����S������Nvendor/symfony/console/Symfony/Component/Console/Tests/Input/ArgvInputTest.php�9���S�9��B�Pvendor/symfony/console/Symfony/Component/Console/Tests/Input/InputOptionTest.php�#���S�#KUöRvendor/symfony/console/Symfony/Component/Console/Tests/Input/InputArgumentTest.php����S�F�5�Ovendor/symfony/console/Symfony/Component/Console/Tests/Input/ArrayInputTest.php<���S<1E��Tvendor/symfony/console/Symfony/Component/Console/Tests/Input/InputDefinitionTest.php�H���S�H��@�Pvendor/symfony/console/Symfony/Component/Console/Tests/Input/StringInputTest.php����S�����Jvendor/symfony/console/Symfony/Component/Console/Tests/Input/InputTest.php����S���p��:vendor/symfony/console/Symfony/Component/Console/README.md����S�~b��8vendor/symfony/console/Symfony/Component/Console/LICENSE)���S)Nm�Q�Pvendor/symfony/console/Symfony/Component/Console/Event/ConsoleTerminateEvent.php ���S �(�Pvendor/symfony/console/Symfony/Component/Console/Event/ConsoleExceptionEvent.php;���S;4�֍�Gvendor/symfony/console/Symfony/Component/Console/Event/ConsoleEvent.php����S���
46 ��Nvendor/symfony/console/Symfony/Component/Console/Event/ConsoleCommandEvent.php����S�u���Gvendor/symfony/console/Symfony/Component/Console/Helper/TableHelper.php�)���S�)" ��Jvendor/symfony/console/Symfony/Component/Console/Helper/ProgressHelper.php�-���S�-Ԭ�;�Bvendor/symfony/console/Symfony/Component/Console/Helper/Helper.php����S�Z\��Lvendor/symfony/console/Symfony/Component/Console/Helper/DescriptorHelper.php� ���S� �կ�Evendor/symfony/console/Symfony/Component/Console/Helper/HelperSet.phpv ���Sv ����Hvendor/symfony/console/Symfony/Component/Console/Helper/DialogHelper.php�?���S�?��Kvendor/symfony/console/Symfony/Component/Console/Helper/HelperInterface.php����S��z�׶Kvendor/symfony/console/Symfony/Component/Console/Helper/FormatterHelper.php����S�g�se�Bvendor/symfony/console/Symfony/Component/Console/ConsoleEvents.php����S�\0�E�\vendor/symfony/console/Symfony/Component/Console/Formatter/OutputFormatterStyleInterface.php����S�5���Svendor/symfony/console/Symfony/Component/Console/Formatter/OutputFormatterStyle.php ���S Ʃ�Wvendor/symfony/console/Symfony/Component/Console/Formatter/OutputFormatterInterface.php����S�Jp�˶Nvendor/symfony/console/Symfony/Component/Console/Formatter/OutputFormatter.php����S��l�Xvendor/symfony/console/Symfony/Component/Console/Formatter/OutputFormatterStyleStack.php�
47 ���S�
48 d�B�Bvendor/symfony/console/Symfony/Component/Console/Output/Output.php����S��D{r�Hvendor/symfony/console/Symfony/Component/Console/Output/StreamOutput.phpM ���SM �X�z�Ivendor/symfony/console/Symfony/Component/Console/Output/ConsoleOutput.php� ���S� e����Kvendor/symfony/console/Symfony/Component/Console/Output/OutputInterface.phpM ���SM ��z��Rvendor/symfony/console/Symfony/Component/Console/Output/ConsoleOutputInterface.php����S�:�2p�Fvendor/symfony/console/Symfony/Component/Console/Output/NullOutput.php����S�F�$_�@vendor/symfony/console/Symfony/Component/Console/Application.phph����Sh��a9m�Nvendor/symfony/console/Symfony/Component/Console/Resources/bin/hiddeninput.exe$���S$���v�=vendor/symfony/console/Symfony/Component/Console/CHANGELOG.md����S�q�]��:vendor/symfony/console/Symfony/Component/Console/Shell.phpF���SF_��Rvendor/symfony/console/Symfony/Component/Console/Descriptor/MarkdownDescriptor.php���S�l��Jvendor/symfony/console/Symfony/Component/Console/Descriptor/Descriptor.php> ���S> {���Svendor/symfony/console/Symfony/Component/Console/Descriptor/DescriptorInterface.php|���S|����Nvendor/symfony/console/Symfony/Component/Console/Descriptor/TextDescriptor.php����S�_F\�Vvendor/symfony/console/Symfony/Component/Console/Descriptor/ApplicationDescription.php� ���S� �s�X�Nvendor/symfony/console/Symfony/Component/Console/Descriptor/JsonDescriptor.php/���S/|�,�Mvendor/symfony/console/Symfony/Component/Console/Descriptor/XmlDescriptor.php�!���S�!���(�>vendor/symfony/console/Symfony/Component/Console/composer.jsone���Se��v��Avendor/symfony/console/Symfony/Component/Console/phpunit.xml.disti���Si?�UH�Hvendor/symfony/console/Symfony/Component/Console/Command/ListCommand.phpV
49 ���SV
50 7Ȕ��Dvendor/symfony/console/Symfony/Component/Console/Command/Command.php>���S> �u�Hvendor/symfony/console/Symfony/Component/Console/Command/HelpCommand.php
51 ���S
52 ���#�Mvendor/symfony/console/Symfony/Component/Console/Tester/ApplicationTester.phpY ���SY A$�ܶIvendor/symfony/console/Symfony/Component/Console/Tester/CommandTester.php ���S ��ֿ�Fvendor/symfony/console/Symfony/Component/Console/Input/StringInput.php�
53 ���S�
54 �΂=�Hvendor/symfony/console/Symfony/Component/Console/Input/InputArgument.php� ���S� ��'�Evendor/symfony/console/Symfony/Component/Console/Input/ArrayInput.php%���S%p����@vendor/symfony/console/Symfony/Component/Console/Input/Input.php����S�(Q�/�Dvendor/symfony/console/Symfony/Component/Console/Input/ArgvInput.php�)���S�)�=(��Ivendor/symfony/console/Symfony/Component/Console/Input/InputInterface.php&���S&o3޶Jvendor/symfony/console/Symfony/Component/Console/Input/InputDefinition.phpN.���SN.�+f�Fvendor/symfony/console/Symfony/Component/Console/Input/InputOption.phpV���SV��OöOvendor/symfony/filesystem/Symfony/Component/Filesystem/Tests/FilesystemTest.php�w���S�w`��@vendor/symfony/filesystem/Symfony/Component/Filesystem/README.md ���S !]��>vendor/symfony/filesystem/Symfony/Component/Filesystem/LICENSE)���S)Nm�Q�Evendor/symfony/filesystem/Symfony/Component/Filesystem/Filesystem.php�C���S�C :嚶Pvendor/symfony/filesystem/Symfony/Component/Filesystem/Exception/IOException.php����S��=鮶Wvendor/symfony/filesystem/Symfony/Component/Filesystem/Exception/ExceptionInterface.php����S�ev��Cvendor/symfony/filesystem/Symfony/Component/Filesystem/CHANGELOG.md6���S6���!�Dvendor/symfony/filesystem/Symfony/Component/Filesystem/composer.json����S�'�Ys�Gvendor/symfony/filesystem/Symfony/Component/Filesystem/phpunit.xml.dist ���S �Ejc�Uvendor/symfony/event-dispatcher/Symfony/Component/EventDispatcher/Tests/EventTest.php ���S �E���\vendor/symfony/event-dispatcher/Symfony/Component/EventDispatcher/Tests/GenericEventTest.php� ���S� O���_vendor/symfony/event-dispatcher/Symfony/Component/EventDispatcher/Tests/EventDispatcherTest.php�,���S�,�)8�mvendor/symfony/event-dispatcher/Symfony/Component/EventDispatcher/Tests/ContainerAwareEventDispatcherTest.php����S��<`�hvendor/symfony/event-dispatcher/Symfony/Component/EventDispatcher/Tests/ImmutableEventDispatcherTest.php� ���S� �+�̶Kvendor/symfony/event-dispatcher/Symfony/Component/EventDispatcher/README.mdV���SV���{�^vendor/symfony/event-dispatcher/Symfony/Component/EventDispatcher/EventDispatcherInterface.php� ���S� U��ҶIvendor/symfony/event-dispatcher/Symfony/Component/EventDispatcher/LICENSE)���S)Nm�Q�Kvendor/symfony/event-dispatcher/Symfony/Component/EventDispatcher/Event.php�
55 ���S�
56 �'~�Uvendor/symfony/event-dispatcher/Symfony/Component/EventDispatcher/EventDispatcher.php����S��ώ��Rvendor/symfony/event-dispatcher/Symfony/Component/EventDispatcher/GenericEvent.phpO���SO?�L��mvendor/symfony/event-dispatcher/Symfony/Component/EventDispatcher/Debug/TraceableEventDispatcherInterface.php����S��
57 ��Nvendor/symfony/event-dispatcher/Symfony/Component/EventDispatcher/CHANGELOG.md����S���7��Ovendor/symfony/event-dispatcher/Symfony/Component/EventDispatcher/composer.json����S�}><q�cvendor/symfony/event-dispatcher/Symfony/Component/EventDispatcher/ContainerAwareEventDispatcher.php���S��Nm�Rvendor/symfony/event-dispatcher/Symfony/Component/EventDispatcher/phpunit.xml.distq���Sq�M�^vendor/symfony/event-dispatcher/Symfony/Component/EventDispatcher/ImmutableEventDispatcher.php����S�s ��^vendor/symfony/event-dispatcher/Symfony/Component/EventDispatcher/EventSubscriberInterface.php1���S1cGKvendor/symfony/process/Symfony/Component/Process/Tests/ProcessUtilsTest.phpf���Sf�7\��Rvendor/symfony/process/Symfony/Component/Process/Tests/PhpExecutableFinderTest.phpM���SM� D��Lvendor/symfony/process/Symfony/Component/Process/Tests/SimpleProcessTest.php� ���S� � "{�Uvendor/symfony/process/Symfony/Component/Process/Tests/ProcessFailedExceptionTest.php�
58 ���S�
59 �WKo�Uvendor/symfony/process/Symfony/Component/Process/Tests/SigchildEnabledProcessTest.php� ���S� @�C$�Nvendor/symfony/process/Symfony/Component/Process/Tests/AbstractProcessTest.php�L���S�Lfz0�Mvendor/symfony/process/Symfony/Component/Process/Tests/ProcessBuilderTest.php}���S}6�"ڶIvendor/symfony/process/Symfony/Component/Process/Tests/SignalListener.php����S�O��Wvendor/symfony/process/Symfony/Component/Process/Tests/ProcessInSigchildEnvironment.php����S�m���Mvendor/symfony/process/Symfony/Component/Process/Tests/NonStopableProcess.php*���S*.D϶Lvendor/symfony/process/Symfony/Component/Process/Tests/ProcessTestHelper.php���SK���Ivendor/symfony/process/Symfony/Component/Process/Tests/PhpProcessTest.php����S�x[�Vvendor/symfony/process/Symfony/Component/Process/Tests/SigchildDisabledProcessTest.php?���S?U�꒶:vendor/symfony/process/Symfony/Component/Process/README.mdP���SP u�˶8vendor/symfony/process/Symfony/Component/Process/LICENSE)���S)Nm�Q�Evendor/symfony/process/Symfony/Component/Process/ExecutableFinder.php
60 ���S
61 �!,$�Cvendor/symfony/process/Symfony/Component/Process/ProcessBuilder.php����S�9%��Avendor/symfony/process/Symfony/Component/Process/ProcessUtils.php ���S � �¶Ovendor/symfony/process/Symfony/Component/Process/Exception/RuntimeException.php����S�>H���Wvendor/symfony/process/Symfony/Component/Process/Exception/InvalidArgumentException.php����S�˅��Mvendor/symfony/process/Symfony/Component/Process/Exception/LogicException.php����S���W�Uvendor/symfony/process/Symfony/Component/Process/Exception/ProcessFailedException.php����S���_�Qvendor/symfony/process/Symfony/Component/Process/Exception/ExceptionInterface.php����S��<��=vendor/symfony/process/Symfony/Component/Process/CHANGELOG.md����S�.)�B�?vendor/symfony/process/Symfony/Component/Process/PhpProcess.php���S@s��Hvendor/symfony/process/Symfony/Component/Process/PhpExecutableFinder.php����S�;�>vendor/symfony/process/Symfony/Component/Process/composer.json����S�-4��Avendor/symfony/process/Symfony/Component/Process/phpunit.xml.dist���Sp\H�<vendor/symfony/process/Symfony/Component/Process/Process.php����S�G�i��$vendor/phpoption/phpoption/README.md����S��{��"vendor/phpoption/phpoption/LICENSE\,���S\,�bE�3vendor/phpoption/phpoption/src/PhpOption/Option.php���SX��7vendor/phpoption/phpoption/src/PhpOption/LazyOption.php� ���S� ��E�1vendor/phpoption/phpoption/src/PhpOption/Some.php�
62 ���S�
63 Up�1vendor/phpoption/phpoption/src/PhpOption/None.phpc���Sc��=��.vendor/phpoption/phpoption/tests/bootstrap.php����S��?�?vendor/phpoption/phpoption/tests/PhpOption/Tests/OptionTest.phpL ���SL 3����=vendor/phpoption/phpoption/tests/PhpOption/Tests/NoneTest.phpQ
64 ���SQ
65 ���S�=vendor/phpoption/phpoption/tests/PhpOption/Tests/SomeTest.php����S�o۶Dvendor/phpoption/phpoption/tests/PhpOption/Tests/PerformanceTest.php|���S|�Qd��Cvendor/phpoption/phpoption/tests/PhpOption/Tests/LazyOptionTest.php-���S-g�Ķ(vendor/phpoption/phpoption/composer.json ���S ԗ%b�+vendor/phpoption/phpoption/phpunit.xml.dist����S���3նvendor/jms/metadata/LICENSE\,���S\,�bE�,vendor/jms/metadata/src/Metadata/Version.php����S����7vendor/jms/metadata/src/Metadata/Driver/DriverChain.phpf���Sf6E�I�7vendor/jms/metadata/src/Metadata/Driver/FileLocator.php����S�6����Hvendor/jms/metadata/src/Metadata/Driver/AdvancedFileLocatorInterface.php����S����?�Cvendor/jms/metadata/src/Metadata/Driver/AdvancedDriverInterface.php����S���T�@vendor/jms/metadata/src/Metadata/Driver/FileLocatorInterface.php���Sځ��=vendor/jms/metadata/src/Metadata/Driver/LazyLoadingDriver.phpH���SH�1�˶>vendor/jms/metadata/src/Metadata/Driver/AbstractFileDriver.php����S��Il�;vendor/jms/metadata/src/Metadata/Driver/DriverInterface.phpW���SW�sgA�4vendor/jms/metadata/src/Metadata/MetadataFactory.phpW���SW�[��4vendor/jms/metadata/src/Metadata/Cache/FileCache.phpm���Sm���9vendor/jms/metadata/src/Metadata/Cache/CacheInterface.php����S�dxm;�?vendor/jms/metadata/src/Metadata/Cache/DoctrineCacheAdapter.php����S�4�޶5vendor/jms/metadata/src/Metadata/PropertyMetadata.php����S���Evendor/jms/metadata/src/Metadata/AdvancedMetadataFactoryInterface.php?���S?BK)<�3vendor/jms/metadata/src/Metadata/MethodMetadata.php����S��o��=vendor/jms/metadata/src/Metadata/MetadataFactoryInterface.phpB���SB�ڌT�2vendor/jms/metadata/src/Metadata/ClassMetadata.php� ���S� � O�;vendor/jms/metadata/src/Metadata/MergeableClassMetadata.phpE���SEQ�t�7vendor/jms/metadata/src/Metadata/MergeableInterface.php����S��ʶ;vendor/jms/metadata/src/Metadata/ClassHierarchyMetadata.php����S�*���'vendor/jms/metadata/tests/bootstrap.php����S�=��Cvendor/jms/metadata/tests/Metadata/Tests/Driver/FileLocatorTest.php�
66 ���S�
67 ��[�Jvendor/jms/metadata/tests/Metadata/Tests/Driver/AbstractFileDriverTest.php ���S � D�Cvendor/jms/metadata/tests/Metadata/Tests/Driver/DriverChainTest.php����S���x�?vendor/jms/metadata/tests/Metadata/Tests/Driver/Fixture/D/D.php���S���?vendor/jms/metadata/tests/Metadata/Tests/Driver/Fixture/D/D.yml���S�?vendor/jms/metadata/tests/Metadata/Tests/Driver/Fixture/B/B.php>���S> ���?vendor/jms/metadata/tests/Metadata/Tests/Driver/Fixture/B/B.yml���S�?vendor/jms/metadata/tests/Metadata/Tests/Driver/Fixture/T/T.xml���S�?vendor/jms/metadata/tests/Metadata/Tests/Driver/Fixture/T/T.php>���S>j �4�Fvendor/jms/metadata/tests/Metadata/Tests/Driver/Fixture/C/SubDir.C.yml���S�Fvendor/jms/metadata/tests/Metadata/Tests/Driver/Fixture/C/SubDir/C.phpE���SE��Y�?vendor/jms/metadata/tests/Metadata/Tests/Driver/Fixture/A/A.xml���S�?vendor/jms/metadata/tests/Metadata/Tests/Driver/Fixture/A/A.php>���S>"jS}�@vendor/jms/metadata/tests/Metadata/Tests/Cache/FileCacheTest.php����S���ㆶKvendor/jms/metadata/tests/Metadata/Tests/Cache/DoctrineCacheAdapterTest.php����S�X*���Gvendor/jms/metadata/tests/Metadata/Tests/MergeableClassMetadataTest.php����S�O`�m�@vendor/jms/metadata/tests/Metadata/Tests/MetadataFactoryTest.php����S�^��y�Avendor/jms/metadata/tests/Metadata/Tests/PropertyMetadataTest.phpe���SeȦZw�@vendor/jms/metadata/tests/Metadata/Tests/Fixtures/TestParent.phpR���SRP�`-�@vendor/jms/metadata/tests/Metadata/Tests/Fixtures/TestObject.php����S�eH,Q�Qvendor/jms/metadata/tests/Metadata/Tests/Fixtures/ComplexHierarchy/InterfaceB.phpU���SU8\���Qvendor/jms/metadata/tests/Metadata/Tests/Fixtures/ComplexHierarchy/InterfaceA.phpU���SU�. �Pvendor/jms/metadata/tests/Metadata/Tests/Fixtures/ComplexHierarchy/BaseClass.php����S�ܾ�?�Pvendor/jms/metadata/tests/Metadata/Tests/Fixtures/ComplexHierarchy/SubClassA.php����S�-Q��Pvendor/jms/metadata/tests/Metadata/Tests/Fixtures/ComplexHierarchy/SubClassB.phps���SsNl�s�>vendor/jms/metadata/tests/Metadata/Tests/ClassMetadataTest.phpm���Sm�Ù��?vendor/jms/metadata/tests/Metadata/Tests/MethodMetadataTest.phpR���SR™�!� vendor/jms/metadata/CHANGELOG.md\���S\��$�!vendor/jms/metadata/composer.jsone���Se+i{�$vendor/jms/metadata/phpunit.xml.dist����S�e��Ѷvendor/jms/metadata/README.rst����S�B���vendor/jms/parser-lib/README.mdn���Sn�g�L�vendor/jms/parser-lib/LICENSE\,���S\,�bE�6vendor/jms/parser-lib/src/JMS/Parser/AbstractLexer.php����S�#� 8�=vendor/jms/parser-lib/src/JMS/Parser/SyntaxErrorException.phpd���Sd�����7vendor/jms/parser-lib/src/JMS/Parser/AbstractParser.phpJ���SJ.�^�4vendor/jms/parser-lib/src/JMS/Parser/SimpleLexer.php&���S&�qs�#vendor/jms/parser-lib/composer.locks���SsK"Gݶ!vendor/jms/parser-lib/doc/LICENSE?���S?F�D�#vendor/jms/parser-lib/doc/index.rstp ���Sp ��L`�)vendor/jms/parser-lib/tests/bootstrap.php����S��?�Bvendor/jms/parser-lib/tests/JMS/Parser/Tests/AbstractLexerTest.php����S�����Cvendor/jms/parser-lib/tests/JMS/Parser/Tests/AbstractParserTest.php� ���S� '{eH�#vendor/jms/parser-lib/composer.json����S�;�ƭ�&vendor/jms/parser-lib/phpunit.xml.dist����S����övendor/jms/serializer/README.md2���S2ge[�vendor/jms/serializer/LICENSE\,���S\,�bE�Evendor/jms/serializer/src/JMS/Serializer/Handler/FormErrorHandler.phpo���SoVoԦ�Dvendor/jms/serializer/src/JMS/Serializer/Handler/HandlerRegistry.php� ���S� ��%��Pvendor/jms/serializer/src/JMS/Serializer/Handler/SubscribingHandlerInterface.php����S�Ի6z�Kvendor/jms/serializer/src/JMS/Serializer/Handler/ArrayCollectionHandler.php
68 ���S
69 ��ޘ�Ovendor/jms/serializer/src/JMS/Serializer/Handler/ConstraintViolationHandler.phpT���ST�"�3�@vendor/jms/serializer/src/JMS/Serializer/Handler/DateHandler.php����S�l��c�Ivendor/jms/serializer/src/JMS/Serializer/Handler/PhpCollectionHandler.php/ ���S/ y�Jl�Hvendor/jms/serializer/src/JMS/Serializer/Handler/LazyHandlerRegistry.php����S��&�0�Mvendor/jms/serializer/src/JMS/Serializer/Handler/HandlerRegistryInterface.phpz���SzqOkܶ7vendor/jms/serializer/src/JMS/Serializer/Serializer.phpl���Sl�تs�Evendor/jms/serializer/src/JMS/Serializer/JsonSerializationVisitor.php���S�7�i�Hvendor/jms/serializer/src/JMS/Serializer/GenericSerializationVisitor.php����S���-�Jvendor/jms/serializer/src/JMS/Serializer/GenericDeserializationVisitor.phpe���Se�<��;vendor/jms/serializer/src/JMS/Serializer/GraphNavigator.phpE3���SE3��oT�Fvendor/jms/serializer/src/JMS/Serializer/XmlDeserializationVisitor.php]%���S]%�ն@vendor/jms/serializer/src/JMS/Serializer/SerializerInterface.php����S�S��"�Avendor/jms/serializer/src/JMS/Serializer/SerializationContext.php= ���S= R�М�<vendor/jms/serializer/src/JMS/Serializer/AbstractVisitor.phpb���Sb+���Cvendor/jms/serializer/src/JMS/Serializer/DeserializationContext.php����S��s��Uvendor/jms/serializer/src/JMS/Serializer/EventDispatcher/EventDispatcherInterface.php'���S'�gǾ�Hvendor/jms/serializer/src/JMS/Serializer/EventDispatcher/ObjectEvent.php����S�$� ��Bvendor/jms/serializer/src/JMS/Serializer/EventDispatcher/Event.php{���S{4�겶Lvendor/jms/serializer/src/JMS/Serializer/EventDispatcher/EventDispatcher.php����S��Н��Cvendor/jms/serializer/src/JMS/Serializer/EventDispatcher/Events.php����S�Y�kĶPvendor/jms/serializer/src/JMS/Serializer/EventDispatcher/PreDeserializeEvent.php����S��{Ͷbvendor/jms/serializer/src/JMS/Serializer/EventDispatcher/Subscriber/SymfonyValidatorSubscriber.phpN���SNy�k-�_vendor/jms/serializer/src/JMS/Serializer/EventDispatcher/Subscriber/DoctrineProxySubscriber.php8���S8y��Nvendor/jms/serializer/src/JMS/Serializer/EventDispatcher/PreSerializeEvent.php����S�o�D��Uvendor/jms/serializer/src/JMS/Serializer/EventDispatcher/EventSubscriberInterface.php����S�� ��Pvendor/jms/serializer/src/JMS/Serializer/EventDispatcher/LazyEventDispatcher.php����S���4vendor/jms/serializer/src/JMS/Serializer/Context.php���S�ʁ�Dvendor/jms/serializer/src/JMS/Serializer/XmlSerializationVisitor.php�+���S�+�G_$�Tvendor/jms/serializer/src/JMS/Serializer/Construction/ObjectConstructorInterface.php����S��Y�l�Svendor/jms/serializer/src/JMS/Serializer/Construction/DoctrineObjectConstructor.php� ���S� �� ��Vvendor/jms/serializer/src/JMS/Serializer/Construction/UnserializeObjectConstructor.php���S�5t��@vendor/jms/serializer/src/JMS/Serializer/Exception/Exception.php!���S!��Ǫ�Gvendor/jms/serializer/src/JMS/Serializer/Exception/RuntimeException.phpU���SU�eV��Ovendor/jms/serializer/src/JMS/Serializer/Exception/InvalidArgumentException.phpm���SmF�Q�Evendor/jms/serializer/src/JMS/Serializer/Exception/LogicException.phpO���SO�[,��Pvendor/jms/serializer/src/JMS/Serializer/Exception/ValidationFailedException.phpJ���SJ�t5��Hvendor/jms/serializer/src/JMS/Serializer/Exception/XmlErrorException.php����S�Ր���Qvendor/jms/serializer/src/JMS/Serializer/Exception/UnsupportedFormatException.php����S�țK0�8vendor/jms/serializer/src/JMS/Serializer/Util/Writer.phpl ���Sl �bF6�Evendor/jms/serializer/src/JMS/Serializer/Twig/SerializerExtension.php����S�pl��7vendor/jms/serializer/src/JMS/Serializer/TypeParser.php� ���S� ��ѶQvendor/jms/serializer/src/JMS/Serializer/Exclusion/ExclusionStrategyInterface.php����S�~�/ܶNvendor/jms/serializer/src/JMS/Serializer/Exclusion/GroupsExclusionStrategy.php����S�tG~ �Mvendor/jms/serializer/src/JMS/Serializer/Exclusion/DepthExclusionStrategy.php����S��_U�Pvendor/jms/serializer/src/JMS/Serializer/Exclusion/DisjunctExclusionStrategy.php�
70 ���S�
71 �E#�Ovendor/jms/serializer/src/JMS/Serializer/Exclusion/VersionExclusionStrategy.php=���S=ʾ�R�=vendor/jms/serializer/src/JMS/Serializer/VisitorInterface.php� ���S� �^�Evendor/jms/serializer/src/JMS/Serializer/YamlSerializationVisitor.php����S������Gvendor/jms/serializer/src/JMS/Serializer/Naming/CacheNamingStrategy.php����S��OF޶Kvendor/jms/serializer/src/JMS/Serializer/Naming/CamelCaseNamingStrategy.php����S��{"h�Svendor/jms/serializer/src/JMS/Serializer/Naming/PropertyNamingStrategyInterface.php����S��_"\�Tvendor/jms/serializer/src/JMS/Serializer/Naming/SerializedNameAnnotationStrategy.phpZ���SZ�q��Svendor/jms/serializer/src/JMS/Serializer/Naming/IdenticalPropertyNamingStrategy.php����S�:�O�Gvendor/jms/serializer/src/JMS/Serializer/Annotation/XmlAttributeMap.php����S�A }@�>vendor/jms/serializer/src/JMS/Serializer/Annotation/XmlMap.phpO���SO�ͅ�Fvendor/jms/serializer/src/JMS/Serializer/Annotation/SerializedName.php&���S&
72 �(�Dvendor/jms/serializer/src/JMS/Serializer/Annotation/PreSerialize.php ���S �m�;�Dvendor/jms/serializer/src/JMS/Serializer/Annotation/XmlAttribute.php����S��>��?vendor/jms/serializer/src/JMS/Serializer/Annotation/Version.php���S�,��Evendor/jms/serializer/src/JMS/Serializer/Annotation/XmlCollection.phpE���SE�L��Bvendor/jms/serializer/src/JMS/Serializer/Annotation/AccessType.phpv���SvL�Q=�Gvendor/jms/serializer/src/JMS/Serializer/Annotation/HandlerCallback.php����S�|T�R�Evendor/jms/serializer/src/JMS/Serializer/Annotation/PostSerialize.php����S�+k��?vendor/jms/serializer/src/JMS/Serializer/Annotation/Exclude.php����S�}�\��Gvendor/jms/serializer/src/JMS/Serializer/Annotation/PostDeserialize.php����S���|�?vendor/jms/serializer/src/JMS/Serializer/Annotation/XmlRoot.php,���S,�T۳�@vendor/jms/serializer/src/JMS/Serializer/Annotation/XmlValue.php����S�Kt��?vendor/jms/serializer/src/JMS/Serializer/Annotation/XmlList.php
73 ���S
74 ��=vendor/jms/serializer/src/JMS/Serializer/Annotation/Until.php���S�ߋ��>vendor/jms/serializer/src/JMS/Serializer/Annotation/Groups.php/���S/y�y�Evendor/jms/serializer/src/JMS/Serializer/Annotation/AccessorOrder.php����S��zW�Gvendor/jms/serializer/src/JMS/Serializer/Annotation/VirtualProperty.php'���S'$֋)�=vendor/jms/serializer/src/JMS/Serializer/Annotation/Since.php���S�{�@vendor/jms/serializer/src/JMS/Serializer/Annotation/MaxDepth.php6���S6�rf#�Evendor/jms/serializer/src/JMS/Serializer/Annotation/Discriminator.php���SQĖ>�<vendor/jms/serializer/src/JMS/Serializer/Annotation/Type.php8���S8cG�@vendor/jms/serializer/src/JMS/Serializer/Annotation/Accessor.php����S��6��Gvendor/jms/serializer/src/JMS/Serializer/Annotation/ExclusionPolicy.php���S"t=�Hvendor/jms/serializer/src/JMS/Serializer/Annotation/XmlKeyValuePairs.php����S�@A\n�>vendor/jms/serializer/src/JMS/Serializer/Annotation/Inline.php����S�07�'�@vendor/jms/serializer/src/JMS/Serializer/Annotation/ReadOnly.php����S��[~�>vendor/jms/serializer/src/JMS/Serializer/Annotation/Expose.php����S��rF�Mvendor/jms/serializer/src/JMS/Serializer/Metadata/VirtualPropertyMetadata.php7
75 ���S7
76 �H6׶Mvendor/jms/serializer/src/JMS/Serializer/Metadata/Driver/AnnotationDriver.php�%���S�%n�f�Fvendor/jms/serializer/src/JMS/Serializer/Metadata/Driver/XmlDriver.php(.���S(.�V�Fvendor/jms/serializer/src/JMS/Serializer/Metadata/Driver/PhpDriver.php���S�X�ѶGvendor/jms/serializer/src/JMS/Serializer/Metadata/Driver/YamlDriver.php�)���S�)��Ovendor/jms/serializer/src/JMS/Serializer/Metadata/Driver/DoctrineTypeDriver.php-���S-����Fvendor/jms/serializer/src/JMS/Serializer/Metadata/PropertyMetadata.php����S��tX�Lvendor/jms/serializer/src/JMS/Serializer/Metadata/StaticPropertyMetadata.php0
77 ���S0
78 (��[�Cvendor/jms/serializer/src/JMS/Serializer/Metadata/ClassMetadata.php�#���S�#��Z!�Gvendor/jms/serializer/src/JMS/Serializer/JsonDeserializationVisitor.php���SCm�>vendor/jms/serializer/src/JMS/Serializer/SerializerBuilder.php�1���S�1��+��#vendor/jms/serializer/composer.lockr����Sr�Xi8�!vendor/jms/serializer/doc/LICENSE?���S?F�D�*vendor/jms/serializer/doc/event_system.rst�
79 ���S�
80 7�=ض#vendor/jms/serializer/doc/index.rstF���SF��0�'vendor/jms/serializer/doc/reference.rstV���SV��/T�&vendor/jms/serializer/doc/cookbook.rstA���SAg�ȟ�#vendor/jms/serializer/doc/usage.rst����S���(?�;vendor/jms/serializer/doc/cookbook/exclusion_strategies.rstO���SO��o�&vendor/jms/serializer/doc/handlers.rst����S�i�z)�3vendor/jms/serializer/doc/reference/annotations.rst�9���S�9[��a�5vendor/jms/serializer/doc/reference/yml_reference.rst���S|���5vendor/jms/serializer/doc/reference/xml_reference.rstW���SWB� �+vendor/jms/serializer/doc/configuration.rst8 ���S8 Y�g�)vendor/jms/serializer/tests/benchmark.php=���S= �fж)vendor/jms/serializer/tests/bootstrap.php:���S:�g\��Avendor/jms/serializer/tests/JMS/Serializer/Tests/Fixtures/Log.phpa���San�YZ�Pvendor/jms/serializer/tests/JMS/Serializer/Tests/Fixtures/CurrencyAwarePrice.phpq���Sq�:i�Mvendor/jms/serializer/tests/JMS/Serializer/Tests/Fixtures/VersionedObject.phpG���SG�7�fvendor/jms/serializer/tests/JMS/Serializer/Tests/Fixtures/ObjectWithVirtualPropertiesAndExcludeAll.php����S�kf�[�Lvendor/jms/serializer/tests/JMS/Serializer/Tests/Fixtures/PersonLocation.php����S������Uvendor/jms/serializer/tests/JMS/Serializer/Tests/Fixtures/CircularReferenceParent.php����S���u�Nvendor/jms/serializer/tests/JMS/Serializer/Tests/Fixtures/PersonCollection.php����S�ސ���Zvendor/jms/serializer/tests/JMS/Serializer/Tests/Fixtures/InitializedObjectConstructor.php����S���:�Pvendor/jms/serializer/tests/JMS/Serializer/Tests/Fixtures/AccessorOrderChild.phpw���Sw�D�Ivendor/jms/serializer/tests/JMS/Serializer/Tests/Fixtures/InlineChild.php����S��1U7�Qvendor/jms/serializer/tests/JMS/Serializer/Tests/Fixtures/Discriminator/Moped.php����S�'�%@�Ovendor/jms/serializer/tests/JMS/Serializer/Tests/Fixtures/Discriminator/Car.php����S���)�Svendor/jms/serializer/tests/JMS/Serializer/Tests/Fixtures/Discriminator/Vehicle.phpP���SPk�}/�Ovendor/jms/serializer/tests/JMS/Serializer/Tests/Fixtures/AllExcludedObject.php����S��.ӶCvendor/jms/serializer/tests/JMS/Serializer/Tests/Fixtures/Input.php9���S9i��:�Qvendor/jms/serializer/tests/JMS/Serializer/Tests/Fixtures/ObjectWithEmptyHash.phpP���SPq~6<�Yvendor/jms/serializer/tests/JMS/Serializer/Tests/Fixtures/CustomDeserializationObject.php����S�C��]�bvendor/jms/serializer/tests/JMS/Serializer/Tests/Fixtures/ObjectWithVersionedVirtualProperties.php����S�kLJ��Lvendor/jms/serializer/tests/JMS/Serializer/Tests/Fixtures/AuthorReadOnly.phpf���SfVQ۶Jvendor/jms/serializer/tests/JMS/Serializer/Tests/Fixtures/GroupsObject.php���S���Dvendor/jms/serializer/tests/JMS/Serializer/Tests/Fixtures/Person.php���Sr]�?�Hvendor/jms/serializer/tests/JMS/Serializer/Tests/Fixtures/AuthorList.php5���S5{�߶Uvendor/jms/serializer/tests/JMS/Serializer/Tests/Fixtures/IndexedCommentsBlogPost.php���S+����Qvendor/jms/serializer/tests/JMS/Serializer/Tests/Fixtures/AccessorOrderMethod.php���S2��̶\vendor/jms/serializer/tests/JMS/Serializer/Tests/Fixtures/ObjectWithVirtualXmlProperties.php� ���S� ��W�Qvendor/jms/serializer/tests/JMS/Serializer/Tests/Fixtures/InvalidGroupsObject.phpe���Se� s:�Jvendor/jms/serializer/tests/JMS/Serializer/Tests/Fixtures/SimpleObject.php����S�cF'V�Cvendor/jms/serializer/tests/JMS/Serializer/Tests/Fixtures/Order.php����S�W]�K�Xvendor/jms/serializer/tests/JMS/Serializer/Tests/Fixtures/ObjectWithXmlKeyValuePairs.php����S�a�[�Bvendor/jms/serializer/tests/JMS/Serializer/Tests/Fixtures/Node.php����S��d�b�Tvendor/jms/serializer/tests/JMS/Serializer/Tests/Fixtures/ObjectWithNullProperty.php0���S0D���Evendor/jms/serializer/tests/JMS/Serializer/Tests/Fixtures/Article.php� ���S� k��o�Fvendor/jms/serializer/tests/JMS/Serializer/Tests/Fixtures/BlogPost.php�
81 ���S�
82 ���Evendor/jms/serializer/tests/JMS/Serializer/Tests/Fixtures/Comment.phpF���SF��N7�Tvendor/jms/serializer/tests/JMS/Serializer/Tests/Fixtures/CircularReferenceChild.php���S])�&�Qvendor/jms/serializer/tests/JMS/Serializer/Tests/Fixtures/AccessorOrderParent.phpC���SCv�ζOvendor/jms/serializer/tests/JMS/Serializer/Tests/Fixtures/SimpleObjectProxy.php[���S[�g톶Jvendor/jms/serializer/tests/JMS/Serializer/Tests/Fixtures/GetSetObject.php}���S}%�Z�Jvendor/jms/serializer/tests/JMS/Serializer/Tests/Fixtures/InlineParent.php����S� ��\�Bvendor/jms/serializer/tests/JMS/Serializer/Tests/Fixtures/Tree.phpt���StZx��Dvendor/jms/serializer/tests/JMS/Serializer/Tests/Fixtures/Author.php���S A?q�Cvendor/jms/serializer/tests/JMS/Serializer/Tests/Fixtures/Price.php����S���
83 :�Tvendor/jms/serializer/tests/JMS/Serializer/Tests/Fixtures/InvalidUsageOfXmlValue.phpM���SM�^v`�Yvendor/jms/serializer/tests/JMS/Serializer/Tests/Fixtures/ObjectWithVirtualProperties.php����S���Ev�Zvendor/jms/serializer/tests/JMS/Serializer/Tests/Fixtures/ObjectWithLifecycleCallbacks.php����S�yG���Ovendor/jms/serializer/tests/JMS/Serializer/Tests/Fixtures/Doctrine/BlogPost.phpC
84 ���SC
85 ^j��Nvendor/jms/serializer/tests/JMS/Serializer/Tests/Fixtures/Doctrine/Comment.phps���SsP"Gl�Mvendor/jms/serializer/tests/JMS/Serializer/Tests/Fixtures/Doctrine/Author.php����S�3�HB�Pvendor/jms/serializer/tests/JMS/Serializer/Tests/Fixtures/CurrencyAwareOrder.php
86 ���S
87 {�w+�Qvendor/jms/serializer/tests/JMS/Serializer/Tests/Twig/SerializerExtensionTest.php3���S3R�� �\vendor/jms/serializer/tests/JMS/Serializer/Tests/Exclusion/DisjunctExclusionStrategyTest.php)���S)���1�Jvendor/jms/serializer/tests/JMS/Serializer/Tests/SerializerBuilderTest.php����S�u�'�Vvendor/jms/serializer/tests/JMS/Serializer/Tests/Serializer/DateIntervalFormatTest.php3���S3 T���Lvendor/jms/serializer/tests/JMS/Serializer/Tests/Serializer/xml/readonly.xml|���S|�qQ�Nvendor/jms/serializer/tests/JMS/Serializer/Tests/Serializer/xml/hash_empty.xmlD���SDu�bm�Rvendor/jms/serializer/tests/JMS/Serializer/Tests/Serializer/xml/virtual_values.xmlN���SN�����Kvendor/jms/serializer/tests/JMS/Serializer/Tests/Serializer/xml/integer.xml:���S:���V�Rvendor/jms/serializer/tests/JMS/Serializer/Tests/Serializer/xml/groups_default.xmlw���Sw�lj9�Ovendor/jms/serializer/tests/JMS/Serializer/Tests/Serializer/xml/form_errors.xml����S��be�Wvendor/jms/serializer/tests/JMS/Serializer/Tests/Serializer/xml/float_trailing_zero.xml:���S:���V�Lvendor/jms/serializer/tests/JMS/Serializer/Tests/Serializer/xml/nullable.xml����S��)�ܶVvendor/jms/serializer/tests/JMS/Serializer/Tests/Serializer/xml/nested_form_errors.xml���S��ͶRvendor/jms/serializer/tests/JMS/Serializer/Tests/Serializer/xml/array_integers.xmls���Ss,H1��Jvendor/jms/serializer/tests/JMS/Serializer/Tests/Serializer/xml/inline.xml����S����Tvendor/jms/serializer/tests/JMS/Serializer/Tests/Serializer/xml/array_key_values.xml����S�|�#�Qvendor/jms/serializer/tests/JMS/Serializer/Tests/Serializer/xml/date_interval.xmlJ���SJ�o銶Nvendor/jms/serializer/tests/JMS/Serializer/Tests/Serializer/xml/groups_foo.xml}���S}���r�Pvendor/jms/serializer/tests/JMS/Serializer/Tests/Serializer/xml/array_floats.xmly���Sy��'��Tvendor/jms/serializer/tests/JMS/Serializer/Tests/Serializer/xml/object_when_null.xmlY���SY�OY�Qvendor/jms/serializer/tests/JMS/Serializer/Tests/Serializer/xml/simple_object.xml����S��]e�Vvendor/jms/serializer/tests/JMS/Serializer/Tests/Serializer/xml/circular_reference.xml����S�B㚹�Gvendor/jms/serializer/tests/JMS/Serializer/Tests/Serializer/xml/car.xmlf���Sf�g �Hvendor/jms/serializer/tests/JMS/Serializer/Tests/Serializer/xml/null.xmlv���Sv�A&��Qvendor/jms/serializer/tests/JMS/Serializer/Tests/Serializer/xml/boolean_false.xml>���S>ia�@�Xvendor/jms/serializer/tests/JMS/Serializer/Tests/Serializer/xml/accessor_order_child.xml����S��
88 8\�Xvendor/jms/serializer/tests/JMS/Serializer/Tests/Serializer/xml/currency_aware_price.xmlJ���SJO���Qvendor/jms/serializer/tests/JMS/Serializer/Tests/Serializer/xml/groups_foobar.xml����S�Y��öVvendor/jms/serializer/tests/JMS/Serializer/Tests/Serializer/xml/virtual_attributes.xml;���S;>!���Zvendor/jms/serializer/tests/JMS/Serializer/Tests/Serializer/xml/accessor_order_methods.xml����S��|5��Yvendor/jms/serializer/tests/JMS/Serializer/Tests/Serializer/xml/accessor_order_parent.xmlh���Sh����Wvendor/jms/serializer/tests/JMS/Serializer/Tests/Serializer/xml/lifecycle_callbacks.xml]���S]�}¶Hvendor/jms/serializer/tests/JMS/Serializer/Tests/Serializer/xml/tree.xml����S��.g��Mvendor/jms/serializer/tests/JMS/Serializer/Tests/Serializer/xml/date_time.xml]���S]��}�[vendor/jms/serializer/tests/JMS/Serializer/Tests/Serializer/xml/virtual_properties_high.xmlK���SKMܮ�Mvendor/jms/serializer/tests/JMS/Serializer/Tests/Serializer/xml/orm_proxy.xml����S�㙈^�cvendor/jms/serializer/tests/JMS/Serializer/Tests/Serializer/xml/order_with_currency_aware_price.xml[���S[�1/�Qvendor/jms/serializer/tests/JMS/Serializer/Tests/Serializer/xml/array_objects.xml:���S:� |�Ivendor/jms/serializer/tests/JMS/Serializer/Tests/Serializer/xml/input.xml\���S\�RE �Ovendor/jms/serializer/tests/JMS/Serializer/Tests/Serializer/xml/array_mixed.xml[���S[bͦ��Mvendor/jms/serializer/tests/JMS/Serializer/Tests/Serializer/xml/blog_post.xmlJ���SJ ~q�Xvendor/jms/serializer/tests/JMS/Serializer/Tests/Serializer/xml/blog_post_unauthored.xml����S�"*p�Uvendor/jms/serializer/tests/JMS/Serializer/Tests/Serializer/xml/person_collection.xml����S���ͶNvendor/jms/serializer/tests/JMS/Serializer/Tests/Serializer/xml/groups_all.xml����S����Zvendor/jms/serializer/tests/JMS/Serializer/Tests/Serializer/xml/virtual_properties_all.xmlZ���SZG=;��Svendor/jms/serializer/tests/JMS/Serializer/Tests/Serializer/xml/custom_accessor.xml����S�1�Ɠ�Gvendor/jms/serializer/tests/JMS/Serializer/Tests/Serializer/xml/log.xml����S��X� �Ivendor/jms/serializer/tests/JMS/Serializer/Tests/Serializer/xml/float.xml>���S>���_�Xvendor/jms/serializer/tests/JMS/Serializer/Tests/Serializer/xml/constraint_violation.xml����S�dG͚�Zvendor/jms/serializer/tests/JMS/Serializer/Tests/Serializer/xml/simple_object_nullable.xml����S��>��Vvendor/jms/serializer/tests/JMS/Serializer/Tests/Serializer/xml/mixed_access_types.xml����S��!�Ivendor/jms/serializer/tests/JMS/Serializer/Tests/Serializer/xml/order.xmlM���SMS�Yض]vendor/jms/serializer/tests/JMS/Serializer/Tests/Serializer/xml/constraint_violation_list.xml���S�q~ǶRvendor/jms/serializer/tests/JMS/Serializer/Tests/Serializer/xml/array_booleans.xmlg���Sg:�Kvendor/jms/serializer/tests/JMS/Serializer/Tests/Serializer/xml/article.xmlC���SClT��Zvendor/jms/serializer/tests/JMS/Serializer/Tests/Serializer/xml/virtual_properties_map.xml����S�5![9�[vendor/jms/serializer/tests/JMS/Serializer/Tests/Serializer/xml/virtual_properties_list.xmlt���St�:f@�Ivendor/jms/serializer/tests/JMS/Serializer/Tests/Serializer/xml/price.xml8���S8�Sc�Vvendor/jms/serializer/tests/JMS/Serializer/Tests/Serializer/xml/virtual_properties.xml����S�8����Qvendor/jms/serializer/tests/JMS/Serializer/Tests/Serializer/xml/array_strings.xml|���S|�Wr�Pvendor/jms/serializer/tests/JMS/Serializer/Tests/Serializer/xml/boolean_true.xml=���S=V�<ضZvendor/jms/serializer/tests/JMS/Serializer/Tests/Serializer/xml/virtual_properties_low.xmlI���SI�8�Svendor/jms/serializer/tests/JMS/Serializer/Tests/Serializer/xml/person_location.xml����S�|+4�cvendor/jms/serializer/tests/JMS/Serializer/Tests/Serializer/xml/object_when_null_and_serialized.xml����S��L�i�Jvendor/jms/serializer/tests/JMS/Serializer/Tests/Serializer/xml/string.xmlH���SH�o��Tvendor/jms/serializer/tests/JMS/Serializer/Tests/Serializer/xml/car_without_type.xmlG���SGN���Rvendor/jms/serializer/tests/JMS/Serializer/Tests/Serializer/GraphNavigatorTest.php(���S(�C>�Uvendor/jms/serializer/tests/JMS/Serializer/Tests/Serializer/YamlSerializationTest.php����S�ׄ5:�Nvendor/jms/serializer/tests/JMS/Serializer/Tests/Serializer/TypeParserTest.phpn ���Sn �:.��Tvendor/jms/serializer/tests/JMS/Serializer/Tests/Serializer/XmlSerializationTest.phpB���SB��?V�Kvendor/jms/serializer/tests/JMS/Serializer/Tests/Serializer/ContextTest.php����S�9í�Uvendor/jms/serializer/tests/JMS/Serializer/Tests/Serializer/JsonSerializationTest.php�%���S�%YZ�cvendor/jms/serializer/tests/JMS/Serializer/Tests/Serializer/EventDispatcher/EventDispatcherTest.php����S�y��yvendor/jms/serializer/tests/JMS/Serializer/Tests/Serializer/EventDispatcher/Subscriber/SymfonyValidatorSubscriberTest.php@���S@2�WC�vvendor/jms/serializer/tests/JMS/Serializer/Tests/Serializer/EventDispatcher/Subscriber/DoctrineProxySubscriberTest.php����S�C!��Kvendor/jms/serializer/tests/JMS/Serializer/Tests/Serializer/yml/integer.yml���SS�Qg�Nvendor/jms/serializer/tests/JMS/Serializer/Tests/Serializer/yml/hash_empty.yml ���S ���M�Zvendor/jms/serializer/tests/JMS/Serializer/Tests/Serializer/yml/accessor_order_methods.yml���S�~�Wvendor/jms/serializer/tests/JMS/Serializer/Tests/Serializer/yml/lifecycle_callbacks.yml���S����Rvendor/jms/serializer/tests/JMS/Serializer/Tests/Serializer/yml/array_integers.yml ���S e��=�Gvendor/jms/serializer/tests/JMS/Serializer/Tests/Serializer/yml/car.yml���S+����Xvendor/jms/serializer/tests/JMS/Serializer/Tests/Serializer/yml/currency_aware_price.yml���S e)�Tvendor/jms/serializer/tests/JMS/Serializer/Tests/Serializer/yml/car_without_type.yml���SK�lW�Qvendor/jms/serializer/tests/JMS/Serializer/Tests/Serializer/yml/groups_foobar.yml!���S!�5 ��Pvendor/jms/serializer/tests/JMS/Serializer/Tests/Serializer/yml/boolean_true.yml���SB#��Rvendor/jms/serializer/tests/JMS/Serializer/Tests/Serializer/yml/groups_default.yml���S<ӗ�Qvendor/jms/serializer/tests/JMS/Serializer/Tests/Serializer/yml/array_strings.yml ���S �ZֶLvendor/jms/serializer/tests/JMS/Serializer/Tests/Serializer/yml/nullable.yml���S��{+�Qvendor/jms/serializer/tests/JMS/Serializer/Tests/Serializer/yml/boolean_false.yml���SW�%�[vendor/jms/serializer/tests/JMS/Serializer/Tests/Serializer/yml/virtual_properties_high.yml���S�e ��Qvendor/jms/serializer/tests/JMS/Serializer/Tests/Serializer/yml/simple_object.yml"���S"���ǶLvendor/jms/serializer/tests/JMS/Serializer/Tests/Serializer/yml/readonly.yml#���S#)�}/�Mvendor/jms/serializer/tests/JMS/Serializer/Tests/Serializer/yml/date_time.yml���Sh��l�Mvendor/jms/serializer/tests/JMS/Serializer/Tests/Serializer/yml/orm_proxy.yml(���S(G���Ivendor/jms/serializer/tests/JMS/Serializer/Tests/Serializer/yml/float.yml���S ����Pvendor/jms/serializer/tests/JMS/Serializer/Tests/Serializer/yml/array_floats.yml���S�owJ�Qvendor/jms/serializer/tests/JMS/Serializer/Tests/Serializer/yml/array_objects.yml`���S`&\ƈ�Vvendor/jms/serializer/tests/JMS/Serializer/Tests/Serializer/yml/circular_reference.yml����S��ѷ�Rvendor/jms/serializer/tests/JMS/Serializer/Tests/Serializer/yml/array_booleans.yml���S ����Zvendor/jms/serializer/tests/JMS/Serializer/Tests/Serializer/yml/virtual_properties_all.yml���S_&IV�Mvendor/jms/serializer/tests/JMS/Serializer/Tests/Serializer/yml/blog_post.yml9���S9Ȼܑ�Gvendor/jms/serializer/tests/JMS/Serializer/Tests/Serializer/yml/log.ymlB���SB7oV϶Ivendor/jms/serializer/tests/JMS/Serializer/Tests/Serializer/yml/order.yml���S���cvendor/jms/serializer/tests/JMS/Serializer/Tests/Serializer/yml/object_when_null_and_serialized.yml���S��[�Kvendor/jms/serializer/tests/JMS/Serializer/Tests/Serializer/yml/article.yml���S[O'�Qvendor/jms/serializer/tests/JMS/Serializer/Tests/Serializer/yml/date_interval.yml���S�P4��Jvendor/jms/serializer/tests/JMS/Serializer/Tests/Serializer/yml/string.yml���S�e2~�Xvendor/jms/serializer/tests/JMS/Serializer/Tests/Serializer/yml/blog_post_unauthored.yml~���S~� p�Jvendor/jms/serializer/tests/JMS/Serializer/Tests/Serializer/yml/inline.yml���S|�.�Hvendor/jms/serializer/tests/JMS/Serializer/Tests/Serializer/yml/tree.yml����S�z�DL�Svendor/jms/serializer/tests/JMS/Serializer/Tests/Serializer/yml/custom_accessor.yml����S��#�C�Ivendor/jms/serializer/tests/JMS/Serializer/Tests/Serializer/yml/input.ymlA���SAR|*b�Nvendor/jms/serializer/tests/JMS/Serializer/Tests/Serializer/yml/groups_foo.yml���SC��ܶcvendor/jms/serializer/tests/JMS/Serializer/Tests/Serializer/yml/order_with_currency_aware_price.yml)���S)�Gl�Zvendor/jms/serializer/tests/JMS/Serializer/Tests/Serializer/yml/simple_object_nullable.yml6���S6�.E^�Zvendor/jms/serializer/tests/JMS/Serializer/Tests/Serializer/yml/virtual_properties_low.yml���S6�y�Nvendor/jms/serializer/tests/JMS/Serializer/Tests/Serializer/yml/groups_all.yml,���S,6 �T�Tvendor/jms/serializer/tests/JMS/Serializer/Tests/Serializer/yml/object_when_null.yml
89 ���S
90 FV�Vvendor/jms/serializer/tests/JMS/Serializer/Tests/Serializer/yml/mixed_access_types.yml,���S,َĶVvendor/jms/serializer/tests/JMS/Serializer/Tests/Serializer/yml/virtual_properties.ymlS���SS�b���Yvendor/jms/serializer/tests/JMS/Serializer/Tests/Serializer/yml/accessor_order_parent.yml
91 ���S
92 &ѫ�Hvendor/jms/serializer/tests/JMS/Serializer/Tests/Serializer/yml/null.yml���Sn��ԶWvendor/jms/serializer/tests/JMS/Serializer/Tests/Serializer/yml/float_trailing_zero.yml���SS�Qg�Xvendor/jms/serializer/tests/JMS/Serializer/Tests/Serializer/yml/accessor_order_child.yml���S_=&��Ovendor/jms/serializer/tests/JMS/Serializer/Tests/Serializer/yml/array_mixed.yml^���S^|��Ivendor/jms/serializer/tests/JMS/Serializer/Tests/Serializer/yml/price.yml ���S b&��jvendor/jms/serializer/tests/JMS/Serializer/Tests/Serializer/Naming/IdenticalPropertyNamingStrategyTest.php����S�QZ�]�Uvendor/jms/serializer/tests/JMS/Serializer/Tests/Serializer/BaseSerializationTest.php�y���S�y�o_�^vendor/jms/serializer/tests/JMS/Serializer/Tests/Metadata/Driver/xml/Discriminator.Vehicle.xml����S� �S�Pvendor/jms/serializer/tests/JMS/Serializer/Tests/Metadata/Driver/xml/invalid.xml"���S"T�#�Nvendor/jms/serializer/tests/JMS/Serializer/Tests/Metadata/Driver/xml/Price.xml����S��f]�Zvendor/jms/serializer/tests/JMS/Serializer/Tests/Metadata/Driver/xml/Discriminator.Car.xml����S�u��dvendor/jms/serializer/tests/JMS/Serializer/Tests/Metadata/Driver/xml/ObjectWithVirtualProperties.xml����S����N�Uvendor/jms/serializer/tests/JMS/Serializer/Tests/Metadata/Driver/xml/GetSetObject.xmlb���Sb����Wvendor/jms/serializer/tests/JMS/Serializer/Tests/Metadata/Driver/xml/AuthorReadOnly.xmlW���SW��n�Qvendor/jms/serializer/tests/JMS/Serializer/Tests/Metadata/Driver/xml/BlogPost.xml����S���ٶ�]vendor/jms/serializer/tests/JMS/Serializer/Tests/Metadata/Driver/xml/exclude_all/BlogPost.xml����S�H��Q�qvendor/jms/serializer/tests/JMS/Serializer/Tests/Metadata/Driver/xml/ObjectWithVirtualPropertiesAndExcludeAll.xml����S�.�ض\vendor/jms/serializer/tests/JMS/Serializer/Tests/Metadata/Driver/xml/Discriminator.Moped.xml����S�%�Ė�^vendor/jms/serializer/tests/JMS/Serializer/Tests/Metadata/Driver/xml/exclude_none/BlogPost.xml����S��8!��cvendor/jms/serializer/tests/JMS/Serializer/Tests/Metadata/Driver/xml/ObjectWithXmlKeyValuePairs.xml����S��r��Mvendor/jms/serializer/tests/JMS/Serializer/Tests/Metadata/Driver/xml/Node.xml����S�%�`��Vvendor/jms/serializer/tests/JMS/Serializer/Tests/Metadata/Driver/xml/case/BlogPost.xml����S�����Yvendor/jms/serializer/tests/JMS/Serializer/Tests/Metadata/Driver/AnnotationDriverTest.php����S����Wvendor/jms/serializer/tests/JMS/Serializer/Tests/Metadata/Driver/DoctrineDriverTest.phpa���SaR��Wvendor/jms/serializer/tests/JMS/Serializer/Tests/Metadata/Driver/yml/AuthorReadOnly.yml+���S+ ��q�dvendor/jms/serializer/tests/JMS/Serializer/Tests/Metadata/Driver/yml/ObjectWithVirtualProperties.yml+���S+{|E�Zvendor/jms/serializer/tests/JMS/Serializer/Tests/Metadata/Driver/yml/accessor/BlogPost.yml����S�X�LضMvendor/jms/serializer/tests/JMS/Serializer/Tests/Metadata/Driver/yml/Node.yml_���S_~��2�Zvendor/jms/serializer/tests/JMS/Serializer/Tests/Metadata/Driver/yml/Discriminator.Car.yml4���S4�rJȶQvendor/jms/serializer/tests/JMS/Serializer/Tests/Metadata/Driver/yml/BlogPost.yml����S���L�qvendor/jms/serializer/tests/JMS/Serializer/Tests/Metadata/Driver/yml/ObjectWithVirtualPropertiesAndExcludeAll.yml����S��Op5�cvendor/jms/serializer/tests/JMS/Serializer/Tests/Metadata/Driver/yml/ObjectWithXmlKeyValuePairs.yml����S� uʶ]vendor/jms/serializer/tests/JMS/Serializer/Tests/Metadata/Driver/yml/exclude_all/BlogPost.yml����S�3 NQ�^vendor/jms/serializer/tests/JMS/Serializer/Tests/Metadata/Driver/yml/Discriminator.Vehicle.yml*���S*��޶^vendor/jms/serializer/tests/JMS/Serializer/Tests/Metadata/Driver/yml/exclude_none/BlogPost.yml����S�oR�[�Vvendor/jms/serializer/tests/JMS/Serializer/Tests/Metadata/Driver/yml/case/BlogPost.yml����S���ob�Nvendor/jms/serializer/tests/JMS/Serializer/Tests/Metadata/Driver/yml/Price.ymlx���Sxaxk��\vendor/jms/serializer/tests/JMS/Serializer/Tests/Metadata/Driver/yml/Discriminator.Moped.yml6���S6A��5�Rvendor/jms/serializer/tests/JMS/Serializer/Tests/Metadata/Driver/PhpDriverTest.php����S�&'��Svendor/jms/serializer/tests/JMS/Serializer/Tests/Metadata/Driver/BaseDriverTest.php����S�J��o�qvendor/jms/serializer/tests/JMS/Serializer/Tests/Metadata/Driver/php/ObjectWithVirtualPropertiesAndExcludeAll.php����S��(���\vendor/jms/serializer/tests/JMS/Serializer/Tests/Metadata/Driver/php/Discriminator.Moped.php����S�YmS�Zvendor/jms/serializer/tests/JMS/Serializer/Tests/Metadata/Driver/php/Discriminator.Car.php����S�#-�Wvendor/jms/serializer/tests/JMS/Serializer/Tests/Metadata/Driver/php/AuthorReadOnly.php����S�z]n}�cvendor/jms/serializer/tests/JMS/Serializer/Tests/Metadata/Driver/php/ObjectWithXmlKeyValuePairs.phpr���Sr�Y��Mvendor/jms/serializer/tests/JMS/Serializer/Tests/Metadata/Driver/php/Node.phpU���SU�k9��Qvendor/jms/serializer/tests/JMS/Serializer/Tests/Metadata/Driver/php/BlogPost.php����S�1���^vendor/jms/serializer/tests/JMS/Serializer/Tests/Metadata/Driver/php/Discriminator.Vehicle.php���S�Nn�Nvendor/jms/serializer/tests/JMS/Serializer/Tests/Metadata/Driver/php/Price.phpu���Su9|O�dvendor/jms/serializer/tests/JMS/Serializer/Tests/Metadata/Driver/php/ObjectWithVirtualProperties.php����S��("��Svendor/jms/serializer/tests/JMS/Serializer/Tests/Metadata/Driver/YamlDriverTest.phpL ���SL {,���Rvendor/jms/serializer/tests/JMS/Serializer/Tests/Metadata/Driver/XmlDriverTest.php
93 ���S
94 L�tѶOvendor/jms/serializer/tests/JMS/Serializer/Tests/Metadata/ClassMetadataTest.php| ���S| �#�}�"vendor/jms/serializer/CHANGELOG.md2���S2(s��#vendor/jms/serializer/composer.json���S�j���&vendor/jms/serializer/phpunit.xml.dist����S��qo̶"vendor/jms/serializer/UPGRADING.md/���S/�����vendor/autoload.php����S��}Ȳ�
95 bin/ocular���S���;�<?php
96
97 namespace Scrutinizer\Ocular\Util;
98
99 use Symfony\Component\Process\Exception\ProcessFailedException;
100 use Symfony\Component\Process\Process;
101
102 class RepositoryIntrospector
103 {
104 const TYPE_GITHUB = 'g';
105 const TYPE_BITBUCKET = 'b';
106
107 private $dir;
108
109 public function __construct($repositoryDir)
110 {
111 $this->dir = $repositoryDir;
112 }
113
114 public function getCurrentRevision()
115 {
116 $proc = new Process('git rev-parse HEAD', $this->dir);
117 if (0 !== $proc->run()) {
118 throw new ProcessFailedException($proc);
119 }
120
121 return trim($proc->getOutput());
122 }
123
124 public function getCurrentParents()
125 {
126 $proc = new Process('git log --pretty="%P" -n1 HEAD', $this->dir);
127 if (0 !== $proc->run()) {
128 throw new ProcessFailedException($proc);
129 }
130
131 return explode(' ', trim($proc->getOutput()));
132 }
133
134 public function getQualifiedName()
135 {
136 $proc = new Process('git remote -v', $this->dir);
137 if (0 !== $proc->run()) {
138 throw new ProcessFailedException($proc);
139 }
140
141 $output = $proc->getOutput();
142
143 $patterns = array(
144 '#^origin\s+(?:git@|(?:git|https?)://)([^:/]+)(?:/|:)([^/]+)/([^/\s]+?)(?:\.git)?(?:\s|\n)#m',
145 '#^[^\s]+\s+(?:git@|(?:git|https?)://)([^:/]+)(?:/|:)([^/]+)/([^/\s]+?)(?:\.git)?(?:\s|\n)#m',
146 );
147
148 foreach ($patterns as $pattern) {
149 if (preg_match($pattern, $output, $match)) {
150 list(, $host, $login, $name) = $match;
151
152 return $this->getRepositoryType($host).'/'.$login.'/'.$name;
153 }
154 }
155
156 throw new \RuntimeException(sprintf("Could not extract repository name from:\n%s", $output));
157 }
158
159 private function getRepositoryType($host)
160 {
161 switch ($host) {
162 case 'github.com':
163 return self::TYPE_GITHUB;
164
165 case 'bitbucket.org':
166 return self::TYPE_BITBUCKET;
167
168 default:
169 throw new \LogicException(sprintf('Unknown host "%s".', $host));
170 }
171 }
172 }<?php
173
174 namespace Scrutinizer\Ocular;
175
176 use PhpOption\Option;
177
178 class Configuration
179 {
180 private $accessToken;
181
182 public function __construct($accessToken = null)
183 {
184 $this->accessToken = $accessToken;
185 }
186
187 public function getAccessToken()
188 {
189 return Option::fromValue($this->accessToken);
190 }
191 }<?php
192
193 namespace Scrutinizer\Ocular;
194
195 use JMS\Serializer\SerializerBuilder;
196 use Symfony\Component\Console\Application;
197 use Symfony\Component\Console\Input\InputOption;
198
199 class Ocular extends Application
200 {
201 const VERSION = '8e0a8c7f085bc4857bd52132833679dcfd504fc1';
202
203 private $cfg;
204
205 public function __construct()
206 {
207 $this->cfg = $this->loadConfiguration();
208 parent::__construct('ocular', self::VERSION);
209
210 $this->registerCommands();
211 }
212
213 protected function getDefaultInputDefinition()
214 {
215 $definition = parent::getDefaultInputDefinition();
216 $definition->addOption(new InputOption('access-token', null, InputOption::VALUE_REQUIRED, 'The access token to use when communicating with scrutinizer-ci.com', $this->cfg->getAccessToken()->getOrElse(null)));
217
218 return $definition;
219 }
220
221 private function registerCommands()
222 {
223 $this->add(new Command\CodeCoverage\UploadCommand());
224 $this->add(new Command\SelfUpdateCommand());
225 }
226
227 private function loadConfiguration()
228 {
229 $homeDir = getenv('HOME');
230 if ( ! is_file($homeDir.'/.ocular/config.json')) {
231 return new Configuration();
232 }
233
234 return SerializerBuilder::create()->build()->deserialize(
235 file_get_contents($homeDir.'/.ocular/config.json'),
236 'Scrutinizer\Ocular\Configuration',
237 'json'
238 );
239 }
240 }<?php
241
242 namespace Scrutinizer\Ocular\Command\CodeCoverage;
243
244 use Guzzle\Http\Exception\BadResponseException;
245 use Guzzle\Http\Exception\ClientErrorResponseException;
246 use Guzzle\Http\Exception\ServerErrorResponseException;
247 use Guzzle\Plugin\Backoff\BackoffPlugin;
248 use Guzzle\Service\Client;
249 use Scrutinizer\Ocular\Util\RepositoryIntrospector;
250 use Symfony\Component\Console\Command\Command;
251 use Symfony\Component\Console\Input\InputArgument;
252 use Symfony\Component\Console\Input\InputInterface;
253 use Symfony\Component\Console\Input\InputOption;
254 use Symfony\Component\Console\Output\OutputInterface;
255 use Symfony\Component\Process\Process;
256
257 class UploadCommand extends Command
258 {
259 protected function configure()
260 {
261 $this
262 ->setName('code-coverage:upload')
263 ->setDescription('Uploads code coverage information for an inspection to Scrutinizer.')
264 ->addArgument('coverage-file', InputArgument::REQUIRED, 'The path to the code coverage file.')
265 ->addOption('api-url', null, InputOption::VALUE_REQUIRED, 'The base URL of the API.', 'https://scrutinizer-ci.com/api')
266 ->addOption('repository', null, InputOption::VALUE_REQUIRED, 'The qualified repository name of your repository (GitHub: g/login/username; Bitbucket: b/login/username).')
267 ->addOption('revision', null, InputOption::VALUE_REQUIRED, 'The revision that the code coverage information belongs to (defaults to git rev-parse HEAD).')
268 ->addOption('format', null, InputOption::VALUE_REQUIRED, 'The format of the code coverage file. Currently supported: php-clover')
269 ->addOption('parent', null, InputOption::VALUE_REQUIRED | InputOption::VALUE_IS_ARRAY, 'The parent revision of the current revision.')
270 ;
271 }
272
273 protected function execute(InputInterface $input, OutputInterface $output)
274 {
275 $repositoryName = $this->parseRepositoryName($input->getOption('repository'));
276
277 $client = new Client($input->getOption('api-url').'{?access_token}', array(
278 'access_token' => $input->getOption('access-token'),
279 'request.options' => array(
280 'Content-Type' => 'application/json',
281 )
282 ));
283 $client->addSubscriber(BackoffPlugin::getExponentialBackoff());
284
285 $postData = $this->generatePostData($input);
286 if ( ! isset($postData['coverage'])) {
287 $output->write(sprintf('Notifying that no code coverage data is available for repository "%s" and revision "%s"... ', $repositoryName, $postData['revision']));
288 } else {
289 $output->write(sprintf('Uploading code coverage for repository "%s" and revision "%s"... ', $repositoryName, $postData['revision']));
290 }
291
292 try {
293 $client->post(
294 'repositories/'.$repositoryName.'/data/code-coverage{?access_token}',
295 null,
296 json_encode($postData)
297 )->send();
298 $output->writeln('Done');
299
300 return 0;
301 } catch (BadResponseException $ex) {
302 $output->writeln("<error>Failed</error>");
303
304 if ($ex instanceof ClientErrorResponseException) {
305 $output->writeln('<error>'.$ex->getResponse()->getBody(true).'</error>');
306
307 return 1;
308 }
309
310 throw $ex;
311 }
312 }
313
314 private function generatePostData(InputInterface $input)
315 {
316 $data = array(
317 'revision' => $this->parseRevision($input->getOption('revision')),
318 'parents' => $this->parseParents($input->getOption('parent')),
319 );
320
321 $coverageFile = $input->getArgument('coverage-file');
322 if (is_file($coverageFile)) {
323 $data['coverage'] = array(
324 'format' => $this->parseFormat($input->getOption('format')),
325 'data' => base64_encode($this->getCoverageData($coverageFile))
326 );
327 }
328
329 return $data;
330 }
331
332 private function getCoverageData($file)
333 {
334 $content = file_get_contents($file);
335 $content = str_replace($this->getBasePath(), '{scrutinizer_project_base_path}', $content);
336
337 return $content;
338 }
339
340 private function getBasePath()
341 {
342 $dir = getcwd();
343 while ( ! empty($dir)) {
344 if (is_dir($dir.'/.git')) {
345 return $dir;
346 }
347
348 $dir = dirname($dir);
349 }
350
351 throw new \LogicException('Could not determine base path for project.');
352 }
353
354 private function parseFormat($format)
355 {
356 if (empty($format)) {
357 throw new \RuntimeException('Please pass the format of the code coverage via the "--format" option, i.e. "--format=php-clover".');
358 }
359
360 return $format;
361 }
362
363 private function parseRepositoryName($name)
364 {
365 if ( ! empty($name)) {
366 return $name;
367 }
368
369 $repoInspector = new RepositoryIntrospector(getcwd());
370
371 return $repoInspector->getQualifiedName();
372 }
373
374 private function parseRevision($inputRevision)
375 {
376 if ( ! empty($inputRevision)) {
377 return $inputRevision;
378 }
379
380 $repoInspector = new RepositoryIntrospector(getcwd());
381
382 return $repoInspector->getCurrentRevision();
383 }
384
385 private function parseParents(array $parents)
386 {
387 if ( ! empty($parents)) {
388 return $parents;
389 }
390
391 $repoInspector = new RepositoryIntrospector(getcwd());
392
393 return $repoInspector->getCurrentParents();
394 }
395 }<?php
396
397 namespace Scrutinizer\Ocular\Command;
398
399 use Scrutinizer\Ocular\Ocular;
400 use Symfony\Component\Console\Command\Command;
401 use Symfony\Component\Console\Input\InputInterface;
402 use Symfony\Component\Console\Output\OutputInterface;
403
404 class SelfUpdateCommand extends Command
405 {
406 protected function configure()
407 {
408 $this
409 ->setName('self-update')
410 ->setDescription('Updates ocular to the latest available version.')
411 ;
412 }
413
414 protected function execute(InputInterface $input, OutputInterface $output)
415 {
416 if (Ocular::VERSION === implode('', array('@', 'revision', '@'))) {
417 throw new \RuntimeException('The "self-update" command is only available for compiled phar files which you can obtain at "https://scrutinizer-ci.com/ocular.phar".');
418 }
419
420 $latest = @file_get_contents('https://scrutinizer-ci.com/ocular.phar.sha1');
421 if (false === $latest) {
422 throw new \RuntimeException(sprintf('Could not fetch latest version. Please try again later.'));
423 }
424
425 if (Ocular::VERSION !== $latest) {
426 $output->writeln(sprintf('Updating from <info>%s</info> to <info>%s</info>.', substr(Ocular::VERSION, 0, 6), substr($latest, 0, 6)));
427
428
429 $tmpFile = tempnam(sys_get_temp_dir(), 'ocular').'.phar';
430 if (false === @copy('https://scrutinizer-ci.com/ocular.phar', $tmpFile)) {
431 throw new \RuntimeException(sprintf('Could not download new version'));
432 }
433
434 // Check download is valid.
435 $phar = new \Phar($tmpFile);
436 unset($phar);
437
438 if (false === @rename($tmpFile, $_SERVER['argv'][0])) {
439 throw new \RuntimeException(sprintf('Could not deploy new file to "%s".', $_SERVER['argv'][0]));
440 }
441 } else {
442 $output->writeln('You are already using the latest version.');
443 }
444 }
445 }# Doctrine Annotations
446
447 [![Build Status](https://travis-ci.org/doctrine/annotations.png?branch=master)](https://travis-ci.org/doctrine/annotations)
448
449 Docblock Annotations Parser library (extracted from Doctrine Common).
450
451 ## Changelog
452
453 ### v1.1
454
455 * Add Exception when ZendOptimizer+ or Opcache is configured to drop comments
456 <?php
457 /*
458 * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
459 * "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
460 * LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR
461 * A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT
462 * OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
463 * SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT
464 * LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
465 * DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
466 * THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
467 * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
468 * OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
469 *
470 * This software consists of voluntary contributions made by many individuals
471 * and is licensed under the MIT license. For more information, see
472 * <http://www.doctrine-project.org>.
473 */
474
475 namespace Doctrine\Common\Annotations;
476
477 use Doctrine\Common\Cache\Cache;
478
479 /**
480 * A cache aware annotation reader.
481 *
482 * @author Johannes M. Schmitt <schmittjoh@gmail.com>
483 * @author Benjamin Eberlei <kontakt@beberlei.de>
484 */
485 final class CachedReader implements Reader
486 {
487 /**
488 * @var string
489 */
490 private static $CACHE_SALT = '@[Annot]';
491
492 /**
493 * @var Reader
494 */
495 private $delegate;
496
497 /**
498 * @var Cache
499 */
500 private $cache;
501
502 /**
503 * @var boolean
504 */
505 private $debug;
506
507 /**
508 * @var array
509 */
510 private $loadedAnnotations;
511
512 /**
513 * Constructor
514 *
515 * @param Reader $reader
516 * @param Cache $cache
517 * @param bool $debug
518 */
519 public function __construct(Reader $reader, Cache $cache, $debug = false)
520 {
521 $this->delegate = $reader;
522 $this->cache = $cache;
523 $this->debug = (Boolean) $debug;
524 }
525
526 /**
527 * Get annotations for class
528 *
529 * @param \ReflectionClass $class
530 * @return array
531 */
532 public function getClassAnnotations(\ReflectionClass $class)
533 {
534 $cacheKey = $class->getName();
535
536 if (isset($this->loadedAnnotations[$cacheKey])) {
537 return $this->loadedAnnotations[$cacheKey];
538 }
539
540 if (false === ($annots = $this->fetchFromCache($cacheKey, $class))) {
541 $annots = $this->delegate->getClassAnnotations($class);
542 $this->saveToCache($cacheKey, $annots);
543 }
544
545 return $this->loadedAnnotations[$cacheKey] = $annots;
546 }
547
548 /**
549 * Get selected annotation for class
550 *
551 * @param \ReflectionClass $class
552 * @param string $annotationName
553 * @return null
554 */
555 public function getClassAnnotation(\ReflectionClass $class, $annotationName)
556 {
557 foreach ($this->getClassAnnotations($class) as $annot) {
558 if ($annot instanceof $annotationName) {
559 return $annot;
560 }
561 }
562
563 return null;
564 }
565
566 /**
567 * Get annotations for property
568 *
569 * @param \ReflectionProperty $property
570 * @return array
571 */
572 public function getPropertyAnnotations(\ReflectionProperty $property)
573 {
574 $class = $property->getDeclaringClass();
575 $cacheKey = $class->getName().'$'.$property->getName();
576
577 if (isset($this->loadedAnnotations[$cacheKey])) {
578 return $this->loadedAnnotations[$cacheKey];
579 }
580
581 if (false === ($annots = $this->fetchFromCache($cacheKey, $class))) {
582 $annots = $this->delegate->getPropertyAnnotations($property);
583 $this->saveToCache($cacheKey, $annots);
584 }
585
586 return $this->loadedAnnotations[$cacheKey] = $annots;
587 }
588
589 /**
590 * Get selected annotation for property
591 *
592 * @param \ReflectionProperty $property
593 * @param string $annotationName
594 * @return null
595 */
596 public function getPropertyAnnotation(\ReflectionProperty $property, $annotationName)
597 {
598 foreach ($this->getPropertyAnnotations($property) as $annot) {
599 if ($annot instanceof $annotationName) {
600 return $annot;
601 }
602 }
603
604 return null;
605 }
606
607 /**
608 * Get method annotations
609 *
610 * @param \ReflectionMethod $method
611 * @return array
612 */
613 public function getMethodAnnotations(\ReflectionMethod $method)
614 {
615 $class = $method->getDeclaringClass();
616 $cacheKey = $class->getName().'#'.$method->getName();
617
618 if (isset($this->loadedAnnotations[$cacheKey])) {
619 return $this->loadedAnnotations[$cacheKey];
620 }
621
622 if (false === ($annots = $this->fetchFromCache($cacheKey, $class))) {
623 $annots = $this->delegate->getMethodAnnotations($method);
624 $this->saveToCache($cacheKey, $annots);
625 }
626
627 return $this->loadedAnnotations[$cacheKey] = $annots;
628 }
629
630 /**
631 * Get selected method annotation
632 *
633 * @param \ReflectionMethod $method
634 * @param string $annotationName
635 * @return null
636 */
637 public function getMethodAnnotation(\ReflectionMethod $method, $annotationName)
638 {
639 foreach ($this->getMethodAnnotations($method) as $annot) {
640 if ($annot instanceof $annotationName) {
641 return $annot;
642 }
643 }
644
645 return null;
646 }
647
648 /**
649 * Clear loaded annotations
650 */
651 public function clearLoadedAnnotations()
652 {
653 $this->loadedAnnotations = array();
654 }
655
656 /**
657 * Fetches a value from the cache.
658 *
659 * @param string $rawCacheKey The cache key.
660 * @param \ReflectionClass $class The related class.
661 * @return mixed|boolean The cached value or false when the value is not in cache.
662 */
663 private function fetchFromCache($rawCacheKey, \ReflectionClass $class)
664 {
665 $cacheKey = $rawCacheKey . self::$CACHE_SALT;
666 if (($data = $this->cache->fetch($cacheKey)) !== false) {
667 if (!$this->debug || $this->isCacheFresh($cacheKey, $class)) {
668 return $data;
669 }
670 }
671
672 return false;
673 }
674
675 /**
676 * Saves a value to the cache
677 *
678 * @param string $rawCacheKey The cache key.
679 * @param mixed $value The value.
680 */
681 private function saveToCache($rawCacheKey, $value)
682 {
683 $cacheKey = $rawCacheKey . self::$CACHE_SALT;
684 $this->cache->save($cacheKey, $value);
685 if ($this->debug) {
686 $this->cache->save('[C]'.$cacheKey, time());
687 }
688 }
689
690 /**
691 * Check if cache is fresh
692 *
693 * @param string $cacheKey
694 * @param \ReflectionClass $class
695 * @return bool
696 */
697 private function isCacheFresh($cacheKey, \ReflectionClass $class)
698 {
699 if (false === $filename = $class->getFilename()) {
700 return true;
701 }
702
703 return $this->cache->fetch('[C]'.$cacheKey) >= filemtime($filename);
704 }
705 }
706 <?php
707 /*
708 * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
709 * "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
710 * LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR
711 * A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT
712 * OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
713 * SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT
714 * LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
715 * DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
716 * THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
717 * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
718 * OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
719 *
720 * This software consists of voluntary contributions made by many individuals
721 * and is licensed under the MIT license. For more information, see
722 * <http://www.doctrine-project.org>.
723 */
724
725 namespace Doctrine\Common\Annotations;
726
727 /**
728 * Parses a file for namespaces/use/class declarations.
729 *
730 * @author Fabien Potencier <fabien@symfony.com>
731 * @author Christian Kaps <christian.kaps@mohiva.com>
732 */
733 class TokenParser
734 {
735 /**
736 * The token list.
737 *
738 * @var array
739 */
740 private $tokens;
741
742 /**
743 * The number of tokens.
744 *
745 * @var int
746 */
747 private $numTokens = 0;
748
749 /**
750 * The current array pointer.
751 *
752 * @var int
753 */
754 private $pointer = 0;
755
756 public function __construct($contents)
757 {
758 $this->tokens = token_get_all($contents);
759 $this->numTokens = count($this->tokens);
760 $this->pointer = 0;
761 }
762
763 /**
764 * Gets the next non whitespace and non comment token.
765 *
766 * @param $docCommentIsComment
767 * If TRUE then a doc comment is considered a comment and skipped.
768 * If FALSE then only whitespace and normal comments are skipped.
769 *
770 * @return array The token if exists, null otherwise.
771 */
772 public function next($docCommentIsComment = TRUE)
773 {
774 for ($i = $this->pointer; $i < $this->numTokens; $i++) {
775 $this->pointer++;
776 if ($this->tokens[$i][0] === T_WHITESPACE ||
777 $this->tokens[$i][0] === T_COMMENT ||
778 ($docCommentIsComment && $this->tokens[$i][0] === T_DOC_COMMENT)) {
779
780 continue;
781 }
782
783 return $this->tokens[$i];
784 }
785
786 return null;
787 }
788
789 /**
790 * Parse a single use statement.
791 *
792 * @return array A list with all found class names for a use statement.
793 */
794 public function parseUseStatement()
795 {
796 $class = '';
797 $alias = '';
798 $statements = array();
799 $explicitAlias = false;
800 while (($token = $this->next())) {
801 $isNameToken = $token[0] === T_STRING || $token[0] === T_NS_SEPARATOR;
802 if (!$explicitAlias && $isNameToken) {
803 $class .= $token[1];
804 $alias = $token[1];
805 } else if ($explicitAlias && $isNameToken) {
806 $alias .= $token[1];
807 } else if ($token[0] === T_AS) {
808 $explicitAlias = true;
809 $alias = '';
810 } else if ($token === ',') {
811 $statements[strtolower($alias)] = $class;
812 $class = '';
813 $alias = '';
814 $explicitAlias = false;
815 } else if ($token === ';') {
816 $statements[strtolower($alias)] = $class;
817 break;
818 } else {
819 break;
820 }
821 }
822
823 return $statements;
824 }
825
826 /**
827 * Get all use statements.
828 *
829 * @param string $namespaceName The namespace name of the reflected class.
830 * @return array A list with all found use statements.
831 */
832 public function parseUseStatements($namespaceName)
833 {
834 $statements = array();
835 while (($token = $this->next())) {
836 if ($token[0] === T_USE) {
837 $statements = array_merge($statements, $this->parseUseStatement());
838 continue;
839 }
840 if ($token[0] !== T_NAMESPACE || $this->parseNamespace() != $namespaceName) {
841 continue;
842 }
843
844 // Get fresh array for new namespace. This is to prevent the parser to collect the use statements
845 // for a previous namespace with the same name. This is the case if a namespace is defined twice
846 // or if a namespace with the same name is commented out.
847 $statements = array();
848 }
849
850 return $statements;
851 }
852
853 /**
854 * Get the namespace.
855 *
856 * @return string The found namespace.
857 */
858 public function parseNamespace()
859 {
860 $name = '';
861 while (($token = $this->next()) && ($token[0] === T_STRING || $token[0] === T_NS_SEPARATOR)) {
862 $name .= $token[1];
863 }
864
865 return $name;
866 }
867
868 /**
869 * Get the class name.
870 *
871 * @return string The foundclass name.
872 */
873 public function parseClass()
874 {
875 // Namespaces and class names are tokenized the same: T_STRINGs
876 // separated by T_NS_SEPARATOR so we can use one function to provide
877 // both.
878 return $this->parseNamespace();
879 }
880 }
881 <?php
882 /*
883 * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
884 * "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
885 * LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR
886 * A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT
887 * OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
888 * SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT
889 * LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
890 * DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
891 * THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
892 * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
893 * OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
894 *
895 * This software consists of voluntary contributions made by many individuals
896 * and is licensed under the MIT license. For more information, see
897 * <http://www.doctrine-project.org>.
898 */
899
900 namespace Doctrine\Common\Annotations;
901
902 use Doctrine\Common\Annotations\Annotation\IgnoreAnnotation;
903 use Doctrine\Common\Annotations\Annotation\Target;
904 use Closure;
905 use ReflectionClass;
906 use ReflectionMethod;
907 use ReflectionProperty;
908
909 /**
910 * A reader for docblock annotations.
911 *
912 * @author Benjamin Eberlei <kontakt@beberlei.de>
913 * @author Guilherme Blanco <guilhermeblanco@hotmail.com>
914 * @author Jonathan Wage <jonwage@gmail.com>
915 * @author Roman Borschel <roman@code-factory.org>
916 * @author Johannes M. Schmitt <schmittjoh@gmail.com>
917 */
918 class AnnotationReader implements Reader
919 {
920 /**
921 * Global map for imports.
922 *
923 * @var array
924 */
925 private static $globalImports = array(
926 'ignoreannotation' => 'Doctrine\Common\Annotations\Annotation\IgnoreAnnotation',
927 );
928
929 /**
930 * A list with annotations that are not causing exceptions when not resolved to an annotation class.
931 *
932 * The names are case sensitive.
933 *
934 * @var array
935 */
936 private static $globalIgnoredNames = array(
937 'access'=> true, 'author'=> true, 'copyright'=> true, 'deprecated'=> true,
938 'example'=> true, 'ignore'=> true, 'internal'=> true, 'link'=> true, 'see'=> true,
939 'since'=> true, 'tutorial'=> true, 'version'=> true, 'package'=> true,
940 'subpackage'=> true, 'name'=> true, 'global'=> true, 'param'=> true,
941 'return'=> true, 'staticvar'=> true, 'category'=> true, 'staticVar'=> true,
942 'static'=> true, 'var'=> true, 'throws'=> true, 'inheritdoc'=> true,
943 'inheritDoc'=> true, 'license'=> true, 'todo'=> true, 'TODO'=> true,
944 'deprec'=> true, 'property' => true, 'method' => true,
945 'abstract'=> true, 'exception'=> true, 'magic' => true, 'api' => true,
946 'final'=> true, 'filesource'=> true, 'throw' => true, 'uses' => true,
947 'usedby'=> true, 'private' => true, 'Annotation' => true, 'override' => true,
948 'codeCoverageIgnore' => true, 'codeCoverageIgnoreStart' => true, 'codeCoverageIgnoreEnd' => true,
949 'Required' => true, 'Attribute' => true, 'Attributes' => true,
950 'Target' => true, 'SuppressWarnings' => true,
951 'ingroup' => true, 'code' => true, 'endcode' => true,
952 'package_version' => true, 'fixme' => true
953 );
954
955 /**
956 * Add a new annotation to the globally ignored annotation names with regard to exception handling.
957 *
958 * @param string $name
959 */
960 static public function addGlobalIgnoredName($name)
961 {
962 self::$globalIgnoredNames[$name] = true;
963 }
964
965 /**
966 * Annotations Parser
967 *
968 * @var \Doctrine\Common\Annotations\DocParser
969 */
970 private $parser;
971
972 /**
973 * Annotations Parser used to collect parsing metadata
974 *
975 * @var \Doctrine\Common\Annotations\DocParser
976 */
977 private $preParser;
978
979 /**
980 * PHP Parser used to collect imports.
981 *
982 * @var \Doctrine\Common\Annotations\PhpParser
983 */
984 private $phpParser;
985
986 /**
987 * In-memory cache mechanism to store imported annotations per class.
988 *
989 * @var array
990 */
991 private $imports = array();
992
993 /**
994 * In-memory cache mechanism to store ignored annotations per class.
995 *
996 * @var array
997 */
998 private $ignoredAnnotationNames = array();
999
1000 /**
1001 * Constructor.
1002 *
1003 * Initializes a new AnnotationReader.
1004 */
1005 public function __construct()
1006 {
1007 if (extension_loaded('Zend Optimizer+') && (ini_get('zend_optimizerplus.save_comments') === "0" || ini_get('opcache.save_comments') === "0")) {
1008 throw AnnotationException::optimizerPlusSaveComments();
1009 }
1010
1011 if (extension_loaded('opcache') && ini_get('opcache.save_comments') == 0) {
1012 throw AnnotationException::optimizerPlusSaveComments();
1013 }
1014
1015 AnnotationRegistry::registerFile(__DIR__ . '/Annotation/IgnoreAnnotation.php');
1016
1017 $this->parser = new DocParser;
1018
1019 $this->preParser = new DocParser;
1020 $this->preParser->setImports(self::$globalImports);
1021 $this->preParser->setIgnoreNotImportedAnnotations(true);
1022
1023 $this->phpParser = new PhpParser;
1024 }
1025
1026 /**
1027 * Gets the annotations applied to a class.
1028 *
1029 * @param ReflectionClass $class The ReflectionClass of the class from which
1030 * the class annotations should be read.
1031 * @return array An array of Annotations.
1032 */
1033 public function getClassAnnotations(ReflectionClass $class)
1034 {
1035 $this->parser->setTarget(Target::TARGET_CLASS);
1036 $this->parser->setImports($this->getImports($class));
1037 $this->parser->setIgnoredAnnotationNames($this->getIgnoredAnnotationNames($class));
1038
1039 return $this->parser->parse($class->getDocComment(), 'class ' . $class->getName());
1040 }
1041
1042 /**
1043 * Gets a class annotation.
1044 *
1045 * @param ReflectionClass $class The ReflectionClass of the class from which
1046 * the class annotations should be read.
1047 * @param string $annotationName The name of the annotation.
1048 * @return mixed The Annotation or NULL, if the requested annotation does not exist.
1049 */
1050 public function getClassAnnotation(ReflectionClass $class, $annotationName)
1051 {
1052 $annotations = $this->getClassAnnotations($class);
1053
1054 foreach ($annotations as $annotation) {
1055 if ($annotation instanceof $annotationName) {
1056 return $annotation;
1057 }
1058 }
1059
1060 return null;
1061 }
1062
1063 /**
1064 * Gets the annotations applied to a property.
1065 *
1066 * @param ReflectionProperty $property The ReflectionProperty of the property
1067 * from which the annotations should be read.
1068 * @return array An array of Annotations.
1069 */
1070 public function getPropertyAnnotations(ReflectionProperty $property)
1071 {
1072 $class = $property->getDeclaringClass();
1073 $context = 'property ' . $class->getName() . "::\$" . $property->getName();
1074 $this->parser->setTarget(Target::TARGET_PROPERTY);
1075 $this->parser->setImports($this->getImports($class));
1076 $this->parser->setIgnoredAnnotationNames($this->getIgnoredAnnotationNames($class));
1077
1078 return $this->parser->parse($property->getDocComment(), $context);
1079 }
1080
1081 /**
1082 * Gets a property annotation.
1083 *
1084 * @param ReflectionProperty $property
1085 * @param string $annotationName The name of the annotation.
1086 * @return mixed The Annotation or NULL, if the requested annotation does not exist.
1087 */
1088 public function getPropertyAnnotation(ReflectionProperty $property, $annotationName)
1089 {
1090 $annotations = $this->getPropertyAnnotations($property);
1091
1092 foreach ($annotations as $annotation) {
1093 if ($annotation instanceof $annotationName) {
1094 return $annotation;
1095 }
1096 }
1097
1098 return null;
1099 }
1100
1101 /**
1102 * Gets the annotations applied to a method.
1103 *
1104 * @param \ReflectionMethod $method The ReflectionMethod of the method from which
1105 * the annotations should be read.
1106 *
1107 * @return array An array of Annotations.
1108 */
1109 public function getMethodAnnotations(ReflectionMethod $method)
1110 {
1111 $class = $method->getDeclaringClass();
1112 $context = 'method ' . $class->getName() . '::' . $method->getName() . '()';
1113 $this->parser->setTarget(Target::TARGET_METHOD);
1114 $this->parser->setImports($this->getImports($class));
1115 $this->parser->setIgnoredAnnotationNames($this->getIgnoredAnnotationNames($class));
1116
1117 return $this->parser->parse($method->getDocComment(), $context);
1118 }
1119
1120 /**
1121 * Gets a method annotation.
1122 *
1123 * @param ReflectionMethod $method
1124 * @param string $annotationName The name of the annotation.
1125 * @return mixed The Annotation or NULL, if the requested annotation does not exist.
1126 */
1127 public function getMethodAnnotation(ReflectionMethod $method, $annotationName)
1128 {
1129 $annotations = $this->getMethodAnnotations($method);
1130
1131 foreach ($annotations as $annotation) {
1132 if ($annotation instanceof $annotationName) {
1133 return $annotation;
1134 }
1135 }
1136
1137 return null;
1138 }
1139
1140 /**
1141 * Returns the ignored annotations for the given class.
1142 *
1143 * @param ReflectionClass $class
1144 * @return array
1145 */
1146 private function getIgnoredAnnotationNames(ReflectionClass $class)
1147 {
1148 if (isset($this->ignoredAnnotationNames[$name = $class->getName()])) {
1149 return $this->ignoredAnnotationNames[$name];
1150 }
1151 $this->collectParsingMetadata($class);
1152
1153 return $this->ignoredAnnotationNames[$name];
1154 }
1155
1156 /**
1157 * Retrieve imports
1158 *
1159 * @param \ReflectionClass $class
1160 * @return array
1161 */
1162 private function getImports(ReflectionClass $class)
1163 {
1164 if (isset($this->imports[$name = $class->getName()])) {
1165 return $this->imports[$name];
1166 }
1167 $this->collectParsingMetadata($class);
1168
1169 return $this->imports[$name];
1170 }
1171
1172 /**
1173 * Collects parsing metadata for a given class
1174 *
1175 * @param ReflectionClass $class
1176 */
1177 private function collectParsingMetadata(ReflectionClass $class)
1178 {
1179 $ignoredAnnotationNames = self::$globalIgnoredNames;
1180
1181 $annotations = $this->preParser->parse($class->getDocComment(), 'class '.$class->name);
1182 foreach ($annotations as $annotation) {
1183 if ($annotation instanceof IgnoreAnnotation) {
1184 foreach ($annotation->names AS $annot) {
1185 $ignoredAnnotationNames[$annot] = true;
1186 }
1187 }
1188 }
1189
1190 $name = $class->getName();
1191 $this->imports[$name] = array_merge(
1192 self::$globalImports,
1193 $this->phpParser->parseClass($class),
1194 array('__NAMESPACE__' => $class->getNamespaceName())
1195 );
1196 $this->ignoredAnnotationNames[$name] = $ignoredAnnotationNames;
1197 }
1198 }
1199 <?php
1200 /*
1201 * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
1202 * "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
1203 * LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR
1204 * A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT
1205 * OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
1206 * SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT
1207 * LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
1208 * DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
1209 * THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
1210 * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
1211 * OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
1212 *
1213 * This software consists of voluntary contributions made by many individuals
1214 * and is licensed under the MIT license. For more information, see
1215 * <http://www.doctrine-project.org>.
1216 */
1217
1218 namespace Doctrine\Common\Annotations;
1219
1220 /**
1221 * AnnotationRegistry
1222 */
1223 final class AnnotationRegistry
1224 {
1225 /**
1226 * A map of namespaces to use for autoloading purposes based on a PSR-0 convention.
1227 *
1228 * Contains the namespace as key and an array of directories as value. If the value is NULL
1229 * the include path is used for checking for the corresponding file.
1230 *
1231 * This autoloading mechanism does not utilize the PHP autoloading but implements autoloading on its own.
1232 *
1233 * @var array
1234 */
1235 static private $autoloadNamespaces = array();
1236
1237 /**
1238 * A map of autoloader callables.
1239 *
1240 * @var array
1241 */
1242 static private $loaders = array();
1243
1244 static public function reset()
1245 {
1246 self::$autoloadNamespaces = array();
1247 self::$loaders = array();
1248 }
1249
1250 /**
1251 * Register file
1252 *
1253 * @param string $file
1254 */
1255 static public function registerFile($file)
1256 {
1257 require_once $file;
1258 }
1259
1260 /**
1261 * Add a namespace with one or many directories to look for files or null for the include path.
1262 *
1263 * Loading of this namespaces will be done with a PSR-0 namespace loading algorithm.
1264 *
1265 * @param string $namespace
1266 * @param string|array|null $dirs
1267 */
1268 static public function registerAutoloadNamespace($namespace, $dirs = null)
1269 {
1270 self::$autoloadNamespaces[$namespace] = $dirs;
1271 }
1272
1273 /**
1274 * Register multiple namespaces
1275 *
1276 * Loading of this namespaces will be done with a PSR-0 namespace loading algorithm.
1277 *
1278 * @param array $namespaces
1279 */
1280 static public function registerAutoloadNamespaces(array $namespaces)
1281 {
1282 self::$autoloadNamespaces = array_merge(self::$autoloadNamespaces, $namespaces);
1283 }
1284
1285 /**
1286 * Register an autoloading callable for annotations, much like spl_autoload_register().
1287 *
1288 * NOTE: These class loaders HAVE to be silent when a class was not found!
1289 * IMPORTANT: Loaders have to return true if they loaded a class that could contain the searched annotation class.
1290 *
1291 * @param callable $callable
1292 *
1293 * @throws \InvalidArgumentException
1294 */
1295 static public function registerLoader($callable)
1296 {
1297 if (!is_callable($callable)) {
1298 throw new \InvalidArgumentException("A callable is expected in AnnotationRegistry::registerLoader().");
1299 }
1300 self::$loaders[] = $callable;
1301 }
1302
1303 /**
1304 * Autoload an annotation class silently.
1305 *
1306 * @param string $class
1307 * @return boolean
1308 */
1309 static public function loadAnnotationClass($class)
1310 {
1311 foreach (self::$autoloadNamespaces AS $namespace => $dirs) {
1312 if (strpos($class, $namespace) === 0) {
1313 $file = str_replace("\\", DIRECTORY_SEPARATOR, $class) . ".php";
1314 if ($dirs === null) {
1315 if ($path = stream_resolve_include_path($file)) {
1316 require $path;
1317 return true;
1318 }
1319 } else {
1320 foreach((array)$dirs AS $dir) {
1321 if (is_file($dir . DIRECTORY_SEPARATOR . $file)) {
1322 require $dir . DIRECTORY_SEPARATOR . $file;
1323 return true;
1324 }
1325 }
1326 }
1327 }
1328 }
1329
1330 foreach (self::$loaders AS $loader) {
1331 if (call_user_func($loader, $class) === true) {
1332 return true;
1333 }
1334 }
1335 return false;
1336 }
1337 }
1338 <?php
1339 /*
1340 * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
1341 * "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
1342 * LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR
1343 * A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT
1344 * OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
1345 * SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT
1346 * LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
1347 * DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
1348 * THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
1349 * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
1350 * OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
1351 *
1352 * This software consists of voluntary contributions made by many individuals
1353 * and is licensed under the MIT license. For more information, see
1354 * <http://www.doctrine-project.org>.
1355 */
1356
1357 namespace Doctrine\Common\Annotations;
1358
1359 use Doctrine\Common\Annotations\Annotation\Target;
1360
1361 /**
1362 * Simple Annotation Reader.
1363 *
1364 * This annotation reader is intended to be used in projects where you have
1365 * full-control over all annotations that are available.
1366 *
1367 * @since 2.2
1368 * @author Johannes M. Schmitt <schmittjoh@gmail.com>
1369 * @author Fabio B. Silva <fabio.bat.silva@gmail.com>
1370 */
1371 class SimpleAnnotationReader implements Reader
1372 {
1373 /**
1374 * @var DocParser
1375 */
1376 private $parser;
1377
1378 /**
1379 * Constructor.
1380 *
1381 * Initializes a new SimpleAnnotationReader.
1382 */
1383 public function __construct()
1384 {
1385 $this->parser = new DocParser();
1386 $this->parser->setIgnoreNotImportedAnnotations(true);
1387 }
1388
1389 /**
1390 * Adds a namespace in which we will look for annotations.
1391 *
1392 * @param string $namespace
1393 */
1394 public function addNamespace($namespace)
1395 {
1396 $this->parser->addNamespace($namespace);
1397 }
1398
1399 /**
1400 * Gets the annotations applied to a class.
1401 *
1402 * @param \ReflectionClass $class The ReflectionClass of the class from which
1403 * the class annotations should be read.
1404 *
1405 * @return array An array of Annotations.
1406 */
1407 public function getClassAnnotations(\ReflectionClass $class)
1408 {
1409 return $this->parser->parse($class->getDocComment(), 'class '.$class->getName());
1410 }
1411
1412 /**
1413 * Gets the annotations applied to a method.
1414 *
1415 * @param \ReflectionMethod $method The ReflectionMethod of the method from which
1416 * the annotations should be read.
1417 *
1418 * @return array An array of Annotations.
1419 */
1420 public function getMethodAnnotations(\ReflectionMethod $method)
1421 {
1422 return $this->parser->parse($method->getDocComment(), 'method '.$method->getDeclaringClass()->name.'::'.$method->getName().'()');
1423 }
1424
1425 /**
1426 * Gets the annotations applied to a property.
1427 *
1428 * @param \ReflectionProperty $property The ReflectionProperty of the property
1429 * from which the annotations should be read.
1430 *
1431 * @return array An array of Annotations.
1432 */
1433 public function getPropertyAnnotations(\ReflectionProperty $property)
1434 {
1435 return $this->parser->parse($property->getDocComment(), 'property '.$property->getDeclaringClass()->name.'::$'.$property->getName());
1436 }
1437
1438 /**
1439 * Gets a class annotation.
1440 *
1441 * @param \ReflectionClass $class The ReflectionClass of the class from which
1442 * the class annotations should be read.
1443 * @param string $annotationName The name of the annotation.
1444 *
1445 * @return mixed The Annotation or NULL, if the requested annotation does not exist.
1446 */
1447 public function getClassAnnotation(\ReflectionClass $class, $annotationName)
1448 {
1449 foreach ($this->getClassAnnotations($class) as $annot) {
1450 if ($annot instanceof $annotationName) {
1451 return $annot;
1452 }
1453 }
1454
1455 return null;
1456 }
1457
1458 /**
1459 * Gets a method annotation.
1460 *
1461 * @param \ReflectionMethod $method
1462 * @param string $annotationName The name of the annotation.
1463 *
1464 * @return mixed The Annotation or NULL, if the requested annotation does not exist.
1465 */
1466 public function getMethodAnnotation(\ReflectionMethod $method, $annotationName)
1467 {
1468 foreach ($this->getMethodAnnotations($method) as $annot) {
1469 if ($annot instanceof $annotationName) {
1470 return $annot;
1471 }
1472 }
1473
1474 return null;
1475 }
1476
1477 /**
1478 * Gets a property annotation.
1479 *
1480 * @param \ReflectionProperty $property
1481 * @param string $annotationName The name of the annotation.
1482 * @return mixed The Annotation or NULL, if the requested annotation does not exist.
1483 */
1484 public function getPropertyAnnotation(\ReflectionProperty $property, $annotationName)
1485 {
1486 foreach ($this->getPropertyAnnotations($property) as $annot) {
1487 if ($annot instanceof $annotationName) {
1488 return $annot;
1489 }
1490 }
1491
1492 return null;
1493 }
1494 }
1495 <?php
1496 /*
1497 * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
1498 * "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
1499 * LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR
1500 * A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT
1501 * OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
1502 * SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT
1503 * LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
1504 * DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
1505 * THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
1506 * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
1507 * OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
1508 *
1509 * This software consists of voluntary contributions made by many individuals
1510 * and is licensed under the MIT license. For more information, see
1511 * <http://www.doctrine-project.org>.
1512 */
1513
1514 namespace Doctrine\Common\Annotations;
1515
1516
1517 /**
1518 * File cache reader for annotations.
1519 *
1520 * @author Johannes M. Schmitt <schmittjoh@gmail.com>
1521 * @author Benjamin Eberlei <kontakt@beberlei.de>
1522 */
1523 class FileCacheReader implements Reader
1524 {
1525 /**
1526 * @var Reader
1527 */
1528 private $reader;
1529
1530 /**
1531 * @var string
1532 */
1533 private $dir;
1534
1535 /**
1536 * @var bool
1537 */
1538 private $debug;
1539
1540 /**
1541 * @var array
1542 */
1543 private $loadedAnnotations = array();
1544
1545 private $classNameHashes = array();
1546
1547 /**
1548 * Constructor
1549 *
1550 * @param Reader $reader
1551 * @param string $cacheDir
1552 * @param bool $debug
1553 *
1554 * @throws \InvalidArgumentException
1555 */
1556 public function __construct(Reader $reader, $cacheDir, $debug = false)
1557 {
1558 $this->reader = $reader;
1559 if (!is_dir($cacheDir) && !@mkdir($cacheDir, 0777, true)) {
1560 throw new \InvalidArgumentException(sprintf('The directory "%s" does not exist and could not be created.', $cacheDir));
1561 }
1562 if (!is_writable($cacheDir)) {
1563 throw new \InvalidArgumentException(sprintf('The directory "%s" is not writable. Both, the webserver and the console user need access. You can manage access rights for multiple users with "chmod +a". If your system does not support this, check out the acl package.', $cacheDir));
1564 }
1565
1566 $this->dir = rtrim($cacheDir, '\\/');
1567 $this->debug = $debug;
1568 }
1569
1570 /**
1571 * Retrieve annotations for class
1572 *
1573 * @param \ReflectionClass $class
1574 * @return array
1575 */
1576 public function getClassAnnotations(\ReflectionClass $class)
1577 {
1578 if ( ! isset($this->classNameHashes[$class->name])) {
1579 $this->classNameHashes[$class->name] = sha1($class->name);
1580 }
1581 $key = $this->classNameHashes[$class->name];
1582
1583 if (isset($this->loadedAnnotations[$key])) {
1584 return $this->loadedAnnotations[$key];
1585 }
1586
1587 $path = $this->dir.'/'.strtr($key, '\\', '-').'.cache.php';
1588 if (!is_file($path)) {
1589 $annot = $this->reader->getClassAnnotations($class);
1590 $this->saveCacheFile($path, $annot);
1591 return $this->loadedAnnotations[$key] = $annot;
1592 }
1593
1594 if ($this->debug
1595 && (false !== $filename = $class->getFilename())
1596 && filemtime($path) < filemtime($filename)) {
1597 @unlink($path);
1598
1599 $annot = $this->reader->getClassAnnotations($class);
1600 $this->saveCacheFile($path, $annot);
1601 return $this->loadedAnnotations[$key] = $annot;
1602 }
1603
1604 return $this->loadedAnnotations[$key] = include $path;
1605 }
1606
1607 /**
1608 * Get annotations for property
1609 *
1610 * @param \ReflectionProperty $property
1611 * @return array
1612 */
1613 public function getPropertyAnnotations(\ReflectionProperty $property)
1614 {
1615 $class = $property->getDeclaringClass();
1616 if ( ! isset($this->classNameHashes[$class->name])) {
1617 $this->classNameHashes[$class->name] = sha1($class->name);
1618 }
1619 $key = $this->classNameHashes[$class->name].'$'.$property->getName();
1620
1621 if (isset($this->loadedAnnotations[$key])) {
1622 return $this->loadedAnnotations[$key];
1623 }
1624
1625 $path = $this->dir.'/'.strtr($key, '\\', '-').'.cache.php';
1626 if (!is_file($path)) {
1627 $annot = $this->reader->getPropertyAnnotations($property);
1628 $this->saveCacheFile($path, $annot);
1629 return $this->loadedAnnotations[$key] = $annot;
1630 }
1631
1632 if ($this->debug
1633 && (false !== $filename = $class->getFilename())
1634 && filemtime($path) < filemtime($filename)) {
1635 @unlink($path);
1636
1637 $annot = $this->reader->getPropertyAnnotations($property);
1638 $this->saveCacheFile($path, $annot);
1639 return $this->loadedAnnotations[$key] = $annot;
1640 }
1641
1642 return $this->loadedAnnotations[$key] = include $path;
1643 }
1644
1645 /**
1646 * Retrieve annotations for method
1647 *
1648 * @param \ReflectionMethod $method
1649 * @return array
1650 */
1651 public function getMethodAnnotations(\ReflectionMethod $method)
1652 {
1653 $class = $method->getDeclaringClass();
1654 if ( ! isset($this->classNameHashes[$class->name])) {
1655 $this->classNameHashes[$class->name] = sha1($class->name);
1656 }
1657 $key = $this->classNameHashes[$class->name].'#'.$method->getName();
1658
1659 if (isset($this->loadedAnnotations[$key])) {
1660 return $this->loadedAnnotations[$key];
1661 }
1662
1663 $path = $this->dir.'/'.strtr($key, '\\', '-').'.cache.php';
1664 if (!is_file($path)) {
1665 $annot = $this->reader->getMethodAnnotations($method);
1666 $this->saveCacheFile($path, $annot);
1667 return $this->loadedAnnotations[$key] = $annot;
1668 }
1669
1670 if ($this->debug
1671 && (false !== $filename = $class->getFilename())
1672 && filemtime($path) < filemtime($filename)) {
1673 @unlink($path);
1674
1675 $annot = $this->reader->getMethodAnnotations($method);
1676 $this->saveCacheFile($path, $annot);
1677 return $this->loadedAnnotations[$key] = $annot;
1678 }
1679
1680 return $this->loadedAnnotations[$key] = include $path;
1681 }
1682
1683 /**
1684 * Save cache file
1685 *
1686 * @param string $path
1687 * @param mixed $data
1688 */
1689 private function saveCacheFile($path, $data)
1690 {
1691 file_put_contents($path, '<?php return unserialize('.var_export(serialize($data), true).');');
1692 }
1693
1694 /**
1695 * Gets a class annotation.
1696 *
1697 * @param \ReflectionClass $class The ReflectionClass of the class from which
1698 * the class annotations should be read.
1699 * @param string $annotationName The name of the annotation.
1700 *
1701 * @return mixed The Annotation or NULL, if the requested annotation does not exist.
1702 */
1703 public function getClassAnnotation(\ReflectionClass $class, $annotationName)
1704 {
1705 $annotations = $this->getClassAnnotations($class);
1706
1707 foreach ($annotations as $annotation) {
1708 if ($annotation instanceof $annotationName) {
1709 return $annotation;
1710 }
1711 }
1712
1713 return null;
1714 }
1715
1716 /**
1717 * Gets a method annotation.
1718 *
1719 * @param \ReflectionMethod $method
1720 * @param string $annotationName The name of the annotation.
1721 * @return mixed The Annotation or NULL, if the requested annotation does not exist.
1722 */
1723 public function getMethodAnnotation(\ReflectionMethod $method, $annotationName)
1724 {
1725 $annotations = $this->getMethodAnnotations($method);
1726
1727 foreach ($annotations as $annotation) {
1728 if ($annotation instanceof $annotationName) {
1729 return $annotation;
1730 }
1731 }
1732
1733 return null;
1734 }
1735
1736 /**
1737 * Gets a property annotation.
1738 *
1739 * @param \ReflectionProperty $property
1740 * @param string $annotationName The name of the annotation.
1741 * @return mixed The Annotation or NULL, if the requested annotation does not exist.
1742 */
1743 public function getPropertyAnnotation(\ReflectionProperty $property, $annotationName)
1744 {
1745 $annotations = $this->getPropertyAnnotations($property);
1746
1747 foreach ($annotations as $annotation) {
1748 if ($annotation instanceof $annotationName) {
1749 return $annotation;
1750 }
1751 }
1752
1753 return null;
1754 }
1755
1756 /**
1757 * Clear stores annotations
1758 */
1759 public function clearLoadedAnnotations()
1760 {
1761 $this->loadedAnnotations = array();
1762 }
1763 }
1764 <?php
1765 /*
1766 * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
1767 * "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
1768 * LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR
1769 * A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT
1770 * OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
1771 * SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT
1772 * LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
1773 * DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
1774 * THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
1775 * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
1776 * OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
1777 *
1778 * This software consists of voluntary contributions made by many individuals
1779 * and is licensed under the MIT license. For more information, see
1780 * <http://www.doctrine-project.org>.
1781 */
1782
1783 namespace Doctrine\Common\Annotations;
1784
1785 /**
1786 * Interface for annotation readers.
1787 *
1788 * @author Johannes M. Schmitt <schmittjoh@gmail.com>
1789 */
1790 interface Reader
1791 {
1792 /**
1793 * @param \ReflectionClass $class
1794 * @return mixed
1795 */
1796 function getClassAnnotations(\ReflectionClass $class);
1797
1798 /**
1799 * @param \ReflectionClass $class
1800 * @param string $annotationName
1801 * @return mixed
1802 */
1803 function getClassAnnotation(\ReflectionClass $class, $annotationName);
1804
1805 /**
1806 * @param \ReflectionMethod $method
1807 * @return mixed
1808 */
1809 function getMethodAnnotations(\ReflectionMethod $method);
1810
1811 /**
1812 * @param \ReflectionMethod $method
1813 * @param string $annotationName
1814 * @return mixed
1815 */
1816 function getMethodAnnotation(\ReflectionMethod $method, $annotationName);
1817
1818 /**
1819 * @param \ReflectionProperty $property
1820 * @return mixed
1821 */
1822 function getPropertyAnnotations(\ReflectionProperty $property);
1823
1824 /**
1825 * @param \ReflectionProperty $property
1826 * @param string $annotationName
1827 * @return mixed
1828 */
1829 function getPropertyAnnotation(\ReflectionProperty $property, $annotationName);
1830 }
1831 <?php
1832 /*
1833 * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
1834 * "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
1835 * LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR
1836 * A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT
1837 * OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
1838 * SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT
1839 * LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
1840 * DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
1841 * THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
1842 * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
1843 * OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
1844 *
1845 * This software consists of voluntary contributions made by many individuals
1846 * and is licensed under the MIT license. For more information, see
1847 * <http://www.doctrine-project.org>.
1848 */
1849
1850 namespace Doctrine\Common\Annotations;
1851
1852 use SplFileObject;
1853
1854 /**
1855 * Parses a file for namespaces/use/class declarations.
1856 *
1857 * @author Fabien Potencier <fabien@symfony.com>
1858 * @author Christian Kaps <christian.kaps@mohiva.com>
1859 */
1860 final class PhpParser
1861 {
1862 /**
1863 * Parses a class.
1864 *
1865 * @param \ReflectionClass $class A <code>ReflectionClass</code> object.
1866 * @return array A list with use statements in the form (Alias => FQN).
1867 */
1868 public function parseClass(\ReflectionClass $class)
1869 {
1870 if (method_exists($class, 'getUseStatements')) {
1871 return $class->getUseStatements();
1872 }
1873
1874 if (false === $filename = $class->getFilename()) {
1875 return array();
1876 }
1877
1878 $content = $this->getFileContent($filename, $class->getStartLine());
1879
1880 if (null === $content) {
1881 return array();
1882 }
1883
1884 $namespace = preg_quote($class->getNamespaceName());
1885 $content = preg_replace('/^.*?(\bnamespace\s+' . $namespace . '\s*[;{].*)$/s', '\\1', $content);
1886 $tokenizer = new TokenParser('<?php ' . $content);
1887
1888 $statements = $tokenizer->parseUseStatements($class->getNamespaceName());
1889
1890 return $statements;
1891 }
1892
1893 /**
1894 * Get the content of the file right up to the given line number.
1895 *
1896 * @param string $filename The name of the file to load.
1897 * @param int $lineNumber The number of lines to read from file.
1898 * @return string The content of the file.
1899 */
1900 private function getFileContent($filename, $lineNumber)
1901 {
1902 if ( ! is_file($filename)) {
1903 return null;
1904 }
1905
1906 $content = '';
1907 $lineCnt = 0;
1908 $file = new SplFileObject($filename);
1909 while (!$file->eof()) {
1910 if ($lineCnt++ == $lineNumber) {
1911 break;
1912 }
1913
1914 $content .= $file->fgets();
1915 }
1916
1917 return $content;
1918 }
1919 }
1920 <?php
1921 /*
1922 * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
1923 * "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
1924 * LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR
1925 * A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT
1926 * OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
1927 * SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT
1928 * LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
1929 * DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
1930 * THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
1931 * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
1932 * OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
1933 *
1934 * This software consists of voluntary contributions made by many individuals
1935 * and is licensed under the MIT license. For more information, see
1936 * <http://www.doctrine-project.org>.
1937 */
1938
1939 namespace Doctrine\Common\Annotations;
1940
1941 use Doctrine\Common\Lexer\AbstractLexer;
1942
1943 /**
1944 * Simple lexer for docblock annotations.
1945 *
1946 * @author Benjamin Eberlei <kontakt@beberlei.de>
1947 * @author Guilherme Blanco <guilhermeblanco@hotmail.com>
1948 * @author Jonathan Wage <jonwage@gmail.com>
1949 * @author Roman Borschel <roman@code-factory.org>
1950 * @author Johannes M. Schmitt <schmittjoh@gmail.com>
1951 */
1952 final class DocLexer extends AbstractLexer
1953 {
1954 const T_NONE = 1;
1955 const T_INTEGER = 2;
1956 const T_STRING = 3;
1957 const T_FLOAT = 4;
1958
1959 // All tokens that are also identifiers should be >= 100
1960 const T_IDENTIFIER = 100;
1961 const T_AT = 101;
1962 const T_CLOSE_CURLY_BRACES = 102;
1963 const T_CLOSE_PARENTHESIS = 103;
1964 const T_COMMA = 104;
1965 const T_EQUALS = 105;
1966 const T_FALSE = 106;
1967 const T_NAMESPACE_SEPARATOR = 107;
1968 const T_OPEN_CURLY_BRACES = 108;
1969 const T_OPEN_PARENTHESIS = 109;
1970 const T_TRUE = 110;
1971 const T_NULL = 111;
1972 const T_COLON = 112;
1973
1974 protected $noCase = array(
1975 '@' => self::T_AT,
1976 ',' => self::T_COMMA,
1977 '(' => self::T_OPEN_PARENTHESIS,
1978 ')' => self::T_CLOSE_PARENTHESIS,
1979 '{' => self::T_OPEN_CURLY_BRACES,
1980 '}' => self::T_CLOSE_CURLY_BRACES,
1981 '=' => self::T_EQUALS,
1982 ':' => self::T_COLON,
1983 '\\' => self::T_NAMESPACE_SEPARATOR
1984 );
1985
1986 protected $withCase = array(
1987 'true' => self::T_TRUE,
1988 'false' => self::T_FALSE,
1989 'null' => self::T_NULL
1990 );
1991
1992 /**
1993 * {@inheritdoc}
1994 */
1995 protected function getCatchablePatterns()
1996 {
1997 return array(
1998 '[a-z_\\\][a-z0-9_\:\\\]*[a-z]{1}',
1999 '(?:[+-]?[0-9]+(?:[\.][0-9]+)*)(?:[eE][+-]?[0-9]+)?',
2000 '"(?:[^"]|"")*"',
2001 );
2002 }
2003
2004 /**
2005 * {@inheritdoc}
2006 */
2007 protected function getNonCatchablePatterns()
2008 {
2009 return array('\s+', '\*+', '(.)');
2010 }
2011
2012 /**
2013 * {@inheritdoc}
2014 *
2015 * @param string $value
2016 *
2017 * @return int
2018 */
2019 protected function getType(&$value)
2020 {
2021 $type = self::T_NONE;
2022
2023 if ($value[0] === '"') {
2024 $value = str_replace('""', '"', substr($value, 1, strlen($value) - 2));
2025
2026 return self::T_STRING;
2027 }
2028
2029 if (isset($this->noCase[$value])) {
2030 return $this->noCase[$value];
2031 }
2032
2033 if ($value[0] === '_' || $value[0] === '\\' || ctype_alpha($value[0])) {
2034 return self::T_IDENTIFIER;
2035 }
2036
2037 $lowerValue = strtolower($value);
2038
2039 if (isset($this->withCase[$lowerValue])) {
2040 return $this->withCase[$lowerValue];
2041 }
2042
2043 // Checking numeric value
2044 if (is_numeric($value)) {
2045 return (strpos($value, '.') !== false || stripos($value, 'e') !== false)
2046 ? self::T_FLOAT : self::T_INTEGER;
2047 }
2048
2049 return $type;
2050 }
2051 }
2052 <?php
2053 /*
2054 * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
2055 * "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
2056 * LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR
2057 * A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT
2058 * OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
2059 * SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT
2060 * LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
2061 * DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
2062 * THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
2063 * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
2064 * OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
2065 *
2066 * This software consists of voluntary contributions made by many individuals
2067 * and is licensed under the MIT license. For more information, see
2068 * <http://www.doctrine-project.org>.
2069 */
2070
2071 namespace Doctrine\Common\Annotations;
2072
2073 /**
2074 * Annotations class
2075 *
2076 * @author Benjamin Eberlei <kontakt@beberlei.de>
2077 * @author Guilherme Blanco <guilhermeblanco@hotmail.com>
2078 * @author Jonathan Wage <jonwage@gmail.com>
2079 * @author Roman Borschel <roman@code-factory.org>
2080 */
2081 class Annotation
2082 {
2083 /**
2084 * Value property. Common among all derived classes.
2085 *
2086 * @var string
2087 */
2088 public $value;
2089
2090 /**
2091 * Constructor
2092 *
2093 * @param array $data Key-value for properties to be defined in this class
2094 */
2095 public final function __construct(array $data)
2096 {
2097 foreach ($data as $key => $value) {
2098 $this->$key = $value;
2099 }
2100 }
2101
2102 /**
2103 * Error handler for unknown property accessor in Annotation class.
2104 *
2105 * @param string $name Unknown property name
2106 *
2107 * @throws \BadMethodCallException
2108 */
2109 public function __get($name)
2110 {
2111 throw new \BadMethodCallException(
2112 sprintf("Unknown property '%s' on annotation '%s'.", $name, get_class($this))
2113 );
2114 }
2115
2116 /**
2117 * Error handler for unknown property mutator in Annotation class.
2118 *
2119 * @param string $name Unkown property name
2120 * @param mixed $value Property value
2121 *
2122 * @throws \BadMethodCallException
2123 */
2124 public function __set($name, $value)
2125 {
2126 throw new \BadMethodCallException(
2127 sprintf("Unknown property '%s' on annotation '%s'.", $name, get_class($this))
2128 );
2129 }
2130 }
2131 <?php
2132 /*
2133 * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
2134 * "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
2135 * LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR
2136 * A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT
2137 * OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
2138 * SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT
2139 * LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
2140 * DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
2141 * THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
2142 * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
2143 * OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
2144 *
2145 * This software consists of voluntary contributions made by many individuals
2146 * and is licensed under the MIT license. For more information, see
2147 * <http://www.doctrine-project.org>.
2148 */
2149
2150 namespace Doctrine\Common\Annotations;
2151
2152 use Doctrine\Common\Annotations\Reader;
2153
2154 /**
2155 * Allows the reader to be used in-place of Doctrine's reader.
2156 *
2157 * @author Johannes M. Schmitt <schmittjoh@gmail.com>
2158 */
2159 class IndexedReader implements Reader
2160 {
2161 /**
2162 * @var Reader
2163 */
2164 private $delegate;
2165
2166 /**
2167 * Constructor
2168 *
2169 * @param Reader $reader
2170 */
2171 public function __construct(Reader $reader)
2172 {
2173 $this->delegate = $reader;
2174 }
2175
2176 /**
2177 * Get Annotations for class
2178 *
2179 * @param \ReflectionClass $class
2180 * @return array
2181 */
2182 public function getClassAnnotations(\ReflectionClass $class)
2183 {
2184 $annotations = array();
2185 foreach ($this->delegate->getClassAnnotations($class) as $annot) {
2186 $annotations[get_class($annot)] = $annot;
2187 }
2188
2189 return $annotations;
2190 }
2191
2192 /**
2193 * Get selected annotation for class
2194 *
2195 * @param \ReflectionClass $class
2196 * @param string $annotation
2197 * @return mixed
2198 */
2199 public function getClassAnnotation(\ReflectionClass $class, $annotation)
2200 {
2201 return $this->delegate->getClassAnnotation($class, $annotation);
2202 }
2203
2204 /**
2205 * Get Annotations for method
2206 *
2207 * @param \ReflectionMethod $method
2208 * @return array
2209 */
2210 public function getMethodAnnotations(\ReflectionMethod $method)
2211 {
2212 $annotations = array();
2213 foreach ($this->delegate->getMethodAnnotations($method) as $annot) {
2214 $annotations[get_class($annot)] = $annot;
2215 }
2216
2217 return $annotations;
2218 }
2219
2220 /**
2221 * Get selected annotation for method
2222 *
2223 * @param \ReflectionMethod $method
2224 * @param string $annotation
2225 * @return mixed
2226 */
2227 public function getMethodAnnotation(\ReflectionMethod $method, $annotation)
2228 {
2229 return $this->delegate->getMethodAnnotation($method, $annotation);
2230 }
2231
2232 /**
2233 * Get annotations for property
2234 *
2235 * @param \ReflectionProperty $property
2236 * @return array
2237 */
2238 public function getPropertyAnnotations(\ReflectionProperty $property)
2239 {
2240 $annotations = array();
2241 foreach ($this->delegate->getPropertyAnnotations($property) as $annot) {
2242 $annotations[get_class($annot)] = $annot;
2243 }
2244
2245 return $annotations;
2246 }
2247
2248 /**
2249 * Get selected annotation for property
2250 *
2251 * @param \ReflectionProperty $property
2252 * @param string $annotation
2253 * @return mixed
2254 */
2255 public function getPropertyAnnotation(\ReflectionProperty $property, $annotation)
2256 {
2257 return $this->delegate->getPropertyAnnotation($property, $annotation);
2258 }
2259
2260 /**
2261 * Proxy all methods to the delegate.
2262 *
2263 * @param string $method
2264 * @param array $args
2265 * @return mixed
2266 */
2267 public function __call($method, $args)
2268 {
2269 return call_user_func_array(array($this->delegate, $method), $args);
2270 }
2271 }
2272 <?php
2273 /*
2274 * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
2275 * "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
2276 * LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR
2277 * A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT
2278 * OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
2279 * SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT
2280 * LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
2281 * DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
2282 * THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
2283 * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
2284 * OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
2285 *
2286 * This software consists of voluntary contributions made by many individuals
2287 * and is licensed under the MIT license. For more information, see
2288 * <http://www.doctrine-project.org>.
2289 */
2290
2291 namespace Doctrine\Common\Annotations;
2292
2293 use Closure;
2294 use ReflectionClass;
2295 use Doctrine\Common\Annotations\Annotation\Enum;
2296 use Doctrine\Common\Annotations\Annotation\Target;
2297 use Doctrine\Common\Annotations\Annotation\Attribute;
2298 use Doctrine\Common\Annotations\Annotation\Attributes;
2299
2300 /**
2301 * A parser for docblock annotations.
2302 *
2303 * It is strongly discouraged to change the default annotation parsing process.
2304 *
2305 * @author Benjamin Eberlei <kontakt@beberlei.de>
2306 * @author Guilherme Blanco <guilhermeblanco@hotmail.com>
2307 * @author Jonathan Wage <jonwage@gmail.com>
2308 * @author Roman Borschel <roman@code-factory.org>
2309 * @author Johannes M. Schmitt <schmittjoh@gmail.com>
2310 * @author Fabio B. Silva <fabio.bat.silva@gmail.com>
2311 */
2312 final class DocParser
2313 {
2314 /**
2315 * An array of all valid tokens for a class name.
2316 *
2317 * @var array
2318 */
2319 private static $classIdentifiers = array(DocLexer::T_IDENTIFIER, DocLexer::T_TRUE, DocLexer::T_FALSE, DocLexer::T_NULL);
2320
2321 /**
2322 * The lexer.
2323 *
2324 * @var \Doctrine\Common\Annotations\DocLexer
2325 */
2326 private $lexer;
2327
2328 /**
2329 * Current target context
2330 *
2331 * @var string
2332 */
2333 private $target;
2334
2335 /**
2336 * Doc Parser used to collect annotation target
2337 *
2338 * @var \Doctrine\Common\Annotations\DocParser
2339 */
2340 private static $metadataParser;
2341
2342 /**
2343 * Flag to control if the current annotation is nested or not.
2344 *
2345 * @var boolean
2346 */
2347 private $isNestedAnnotation = false;
2348
2349 /**
2350 * Hashmap containing all use-statements that are to be used when parsing
2351 * the given doc block.
2352 *
2353 * @var array
2354 */
2355 private $imports = array();
2356
2357 /**
2358 * This hashmap is used internally to cache results of class_exists()
2359 * look-ups.
2360 *
2361 * @var array
2362 */
2363 private $classExists = array();
2364
2365 /**
2366 * Whether annotations that have not been imported should be ignored.
2367 *
2368 * @var boolean
2369 */
2370 private $ignoreNotImportedAnnotations = false;
2371
2372 /**
2373 * An array of default namespaces if operating in simple mode.
2374 *
2375 * @var array
2376 */
2377 private $namespaces = array();
2378
2379 /**
2380 * A list with annotations that are not causing exceptions when not resolved to an annotation class.
2381 *
2382 * The names must be the raw names as used in the class, not the fully qualified
2383 * class names.
2384 *
2385 * @var array
2386 */
2387 private $ignoredAnnotationNames = array();
2388
2389 /**
2390 * @var string
2391 */
2392 private $context = '';
2393
2394 /**
2395 * Hash-map for caching annotation metadata
2396 * @var array
2397 */
2398 private static $annotationMetadata = array(
2399 'Doctrine\Common\Annotations\Annotation\Target' => array(
2400 'is_annotation' => true,
2401 'has_constructor' => true,
2402 'properties' => array(),
2403 'targets_literal' => 'ANNOTATION_CLASS',
2404 'targets' => Target::TARGET_CLASS,
2405 'default_property' => 'value',
2406 'attribute_types' => array(
2407 'value' => array(
2408 'required' => false,
2409 'type' =>'array',
2410 'array_type'=>'string',
2411 'value' =>'array<string>'
2412 )
2413 ),
2414 ),
2415 'Doctrine\Common\Annotations\Annotation\Attribute' => array(
2416 'is_annotation' => true,
2417 'has_constructor' => false,
2418 'targets_literal' => 'ANNOTATION_ANNOTATION',
2419 'targets' => Target::TARGET_ANNOTATION,
2420 'default_property' => 'name',
2421 'properties' => array(
2422 'name' => 'name',
2423 'type' => 'type',
2424 'required' => 'required'
2425 ),
2426 'attribute_types' => array(
2427 'value' => array(
2428 'required' => true,
2429 'type' =>'string',
2430 'value' =>'string'
2431 ),
2432 'type' => array(
2433 'required' =>true,
2434 'type' =>'string',
2435 'value' =>'string'
2436 ),
2437 'required' => array(
2438 'required' =>false,
2439 'type' =>'boolean',
2440 'value' =>'boolean'
2441 )
2442 ),
2443 ),
2444 'Doctrine\Common\Annotations\Annotation\Attributes' => array(
2445 'is_annotation' => true,
2446 'has_constructor' => false,
2447 'targets_literal' => 'ANNOTATION_CLASS',
2448 'targets' => Target::TARGET_CLASS,
2449 'default_property' => 'value',
2450 'properties' => array(
2451 'value' => 'value'
2452 ),
2453 'attribute_types' => array(
2454 'value' => array(
2455 'type' =>'array',
2456 'required' =>true,
2457 'array_type'=>'Doctrine\Common\Annotations\Annotation\Attribute',
2458 'value' =>'array<Doctrine\Common\Annotations\Annotation\Attribute>'
2459 )
2460 ),
2461 ),
2462 'Doctrine\Common\Annotations\Annotation\Enum' => array(
2463 'is_annotation' => true,
2464 'has_constructor' => true,
2465 'targets_literal' => 'ANNOTATION_PROPERTY',
2466 'targets' => Target::TARGET_PROPERTY,
2467 'default_property' => 'value',
2468 'properties' => array(
2469 'value' => 'value'
2470 ),
2471 'attribute_types' => array(
2472 'value' => array(
2473 'type' => 'array',
2474 'required' => true,
2475 ),
2476 'literal' => array(
2477 'type' => 'array',
2478 'required' => false,
2479 ),
2480 ),
2481 ),
2482 );
2483
2484 /**
2485 * Hash-map for handle types declaration
2486 *
2487 * @var array
2488 */
2489 private static $typeMap = array(
2490 'float' => 'double',
2491 'bool' => 'boolean',
2492 // allow uppercase Boolean in honor of George Boole
2493 'Boolean' => 'boolean',
2494 'int' => 'integer',
2495 );
2496
2497 /**
2498 * Constructs a new DocParser.
2499 */
2500 public function __construct()
2501 {
2502 $this->lexer = new DocLexer;
2503 }
2504
2505 /**
2506 * Sets the annotation names that are ignored during the parsing process.
2507 *
2508 * The names are supposed to be the raw names as used in the class, not the
2509 * fully qualified class names.
2510 *
2511 * @param array $names
2512 */
2513 public function setIgnoredAnnotationNames(array $names)
2514 {
2515 $this->ignoredAnnotationNames = $names;
2516 }
2517
2518 /**
2519 * Sets ignore on not-imported annotations
2520 *
2521 * @param $bool
2522 */
2523 public function setIgnoreNotImportedAnnotations($bool)
2524 {
2525 $this->ignoreNotImportedAnnotations = (Boolean) $bool;
2526 }
2527
2528 /**
2529 * Sets the default namespaces.
2530 *
2531 * @param array $namespace
2532 *
2533 * @throws \RuntimeException
2534 */
2535 public function addNamespace($namespace)
2536 {
2537 if ($this->imports) {
2538 throw new \RuntimeException('You must either use addNamespace(), or setImports(), but not both.');
2539 }
2540 $this->namespaces[] = $namespace;
2541 }
2542
2543 /**
2544 * Sets the imports
2545 *
2546 * @param array $imports
2547 * @throws \RuntimeException
2548 */
2549 public function setImports(array $imports)
2550 {
2551 if ($this->namespaces) {
2552 throw new \RuntimeException('You must either use addNamespace(), or setImports(), but not both.');
2553 }
2554 $this->imports = $imports;
2555 }
2556
2557 /**
2558 * Sets current target context as bitmask.
2559 *
2560 * @param integer $target
2561 */
2562 public function setTarget($target)
2563 {
2564 $this->target = $target;
2565 }
2566
2567 /**
2568 * Parses the given docblock string for annotations.
2569 *
2570 * @param string $input The docblock string to parse.
2571 * @param string $context The parsing context.
2572 * @return array Array of annotations. If no annotations are found, an empty array is returned.
2573 */
2574 public function parse($input, $context = '')
2575 {
2576 if (false === $pos = strpos($input, '@')) {
2577 return array();
2578 }
2579
2580 // also parse whatever character is before the @
2581 if ($pos > 0) {
2582 $pos -= 1;
2583 }
2584
2585 $this->context = $context;
2586 $this->lexer->setInput(trim(substr($input, $pos), '* /'));
2587 $this->lexer->moveNext();
2588
2589 return $this->Annotations();
2590 }
2591
2592 /**
2593 * Attempts to match the given token with the current lookahead token.
2594 * If they match, updates the lookahead token; otherwise raises a syntax error.
2595 *
2596 * @param int $token type of Token.
2597 * @return bool True if tokens match; false otherwise.
2598 */
2599 private function match($token)
2600 {
2601 if ( ! $this->lexer->isNextToken($token) ) {
2602 $this->syntaxError($this->lexer->getLiteral($token));
2603 }
2604
2605 return $this->lexer->moveNext();
2606 }
2607
2608 /**
2609 * Attempts to match the current lookahead token with any of the given tokens.
2610 *
2611 * If any of them matches, this method updates the lookahead token; otherwise
2612 * a syntax error is raised.
2613 *
2614 * @param array $tokens
2615 * @return bool
2616 */
2617 private function matchAny(array $tokens)
2618 {
2619 if ( ! $this->lexer->isNextTokenAny($tokens)) {
2620 $this->syntaxError(implode(' or ', array_map(array($this->lexer, 'getLiteral'), $tokens)));
2621 }
2622
2623 return $this->lexer->moveNext();
2624 }
2625
2626 /**
2627 * Generates a new syntax error.
2628 *
2629 * @param string $expected Expected string.
2630 * @param array $token Optional token.
2631 *
2632 * @throws AnnotationException
2633 */
2634 private function syntaxError($expected, $token = null)
2635 {
2636 if ($token === null) {
2637 $token = $this->lexer->lookahead;
2638 }
2639
2640 $message = "Expected {$expected}, got ";
2641
2642 if ($this->lexer->lookahead === null) {
2643 $message .= 'end of string';
2644 } else {
2645 $message .= "'{$token['value']}' at position {$token['position']}";
2646 }
2647
2648 if (strlen($this->context)) {
2649 $message .= ' in ' . $this->context;
2650 }
2651
2652 $message .= '.';
2653
2654 throw AnnotationException::syntaxError($message);
2655 }
2656
2657 /**
2658 * Attempt to check if a class exists or not. This never goes through the PHP autoloading mechanism
2659 * but uses the {@link AnnotationRegistry} to load classes.
2660 *
2661 * @param string $fqcn
2662 * @return boolean
2663 */
2664 private function classExists($fqcn)
2665 {
2666 if (isset($this->classExists[$fqcn])) {
2667 return $this->classExists[$fqcn];
2668 }
2669
2670 // first check if the class already exists, maybe loaded through another AnnotationReader
2671 if (class_exists($fqcn, false)) {
2672 return $this->classExists[$fqcn] = true;
2673 }
2674
2675 // final check, does this class exist?
2676 return $this->classExists[$fqcn] = AnnotationRegistry::loadAnnotationClass($fqcn);
2677 }
2678
2679 /**
2680 * Collects parsing metadata for a given annotation class
2681 *
2682 * @param string $name The annotation name
2683 */
2684 private function collectAnnotationMetadata($name)
2685 {
2686 if (self::$metadataParser == null){
2687 self::$metadataParser = new self();
2688 self::$metadataParser->setIgnoreNotImportedAnnotations(true);
2689 self::$metadataParser->setIgnoredAnnotationNames($this->ignoredAnnotationNames);
2690 self::$metadataParser->setImports(array(
2691 'enum' => 'Doctrine\Common\Annotations\Annotation\Enum',
2692 'target' => 'Doctrine\Common\Annotations\Annotation\Target',
2693 'attribute' => 'Doctrine\Common\Annotations\Annotation\Attribute',
2694 'attributes' => 'Doctrine\Common\Annotations\Annotation\Attributes'
2695 ));
2696 AnnotationRegistry::registerFile(__DIR__ . '/Annotation/Enum.php');
2697 AnnotationRegistry::registerFile(__DIR__ . '/Annotation/Target.php');
2698 AnnotationRegistry::registerFile(__DIR__ . '/Annotation/Attribute.php');
2699 AnnotationRegistry::registerFile(__DIR__ . '/Annotation/Attributes.php');
2700 }
2701
2702 $class = new \ReflectionClass($name);
2703 $docComment = $class->getDocComment();
2704
2705 // Sets default values for annotation metadata
2706 $metadata = array(
2707 'default_property' => null,
2708 'has_constructor' => (null !== $constructor = $class->getConstructor()) && $constructor->getNumberOfParameters() > 0,
2709 'properties' => array(),
2710 'property_types' => array(),
2711 'attribute_types' => array(),
2712 'targets_literal' => null,
2713 'targets' => Target::TARGET_ALL,
2714 'is_annotation' => false !== strpos($docComment, '@Annotation'),
2715 );
2716
2717 // verify that the class is really meant to be an annotation
2718 if ($metadata['is_annotation']) {
2719
2720 self::$metadataParser->setTarget(Target::TARGET_CLASS);
2721
2722 foreach (self::$metadataParser->parse($docComment, 'class @' . $name) as $annotation) {
2723 if ($annotation instanceof Target) {
2724 $metadata['targets'] = $annotation->targets;
2725 $metadata['targets_literal'] = $annotation->literal;
2726
2727 } elseif ($annotation instanceof Attributes) {
2728 foreach ($annotation->value as $attrib) {
2729 // handle internal type declaration
2730 $type = isset(self::$typeMap[$attrib->type]) ? self::$typeMap[$attrib->type] : $attrib->type;
2731
2732 // handle the case if the property type is mixed
2733 if ('mixed' !== $type) {
2734 // Checks if the property has array<type>
2735 if (false !== $pos = strpos($type, '<')) {
2736 $arrayType = substr($type, $pos+1, -1);
2737 $type = 'array';
2738
2739 if (isset(self::$typeMap[$arrayType])) {
2740 $arrayType = self::$typeMap[$arrayType];
2741 }
2742
2743 $metadata['attribute_types'][$attrib->name]['array_type'] = $arrayType;
2744 }
2745
2746 $metadata['attribute_types'][$attrib->name]['type'] = $type;
2747 $metadata['attribute_types'][$attrib->name]['value'] = $attrib->type;
2748 $metadata['attribute_types'][$attrib->name]['required'] = $attrib->required;
2749 }
2750 }
2751 }
2752 }
2753
2754 // if not has a constructor will inject values into public properties
2755 if (false === $metadata['has_constructor']) {
2756 // collect all public properties
2757 foreach ($class->getProperties(\ReflectionProperty::IS_PUBLIC) as $property) {
2758 $metadata['properties'][$property->name] = $property->name;
2759
2760 if(false === ($propertyComment = $property->getDocComment())) {
2761 continue;
2762 }
2763
2764 // checks if the property has @var annotation
2765 if (false !== strpos($propertyComment, '@var')
2766 && preg_match('/@var\s+([^\s]+)/',$propertyComment, $matches)) {
2767 // literal type declaration
2768 $value = $matches[1];
2769
2770 // handle internal type declaration
2771 $type = isset(self::$typeMap[$value]) ? self::$typeMap[$value] : $value;
2772
2773 // handle the case if the property type is mixed
2774 if ('mixed' !== $type) {
2775 // Checks if the property has @var array<type> annotation
2776 if (false !== $pos = strpos($type, '<')) {
2777 $arrayType = substr($type, $pos+1, -1);
2778 $type = 'array';
2779
2780 if (isset(self::$typeMap[$arrayType])) {
2781 $arrayType = self::$typeMap[$arrayType];
2782 }
2783
2784 $metadata['attribute_types'][$property->name]['array_type'] = $arrayType;
2785 }
2786
2787 $metadata['attribute_types'][$property->name]['type'] = $type;
2788 $metadata['attribute_types'][$property->name]['value'] = $value;
2789 $metadata['attribute_types'][$property->name]['required'] = false !== strpos($propertyComment, '@Required');
2790 }
2791 }
2792
2793 // checks if the property has @Enum
2794 if (false !== strpos($propertyComment, '@Enum')){
2795
2796 $context = 'property ' . $class->name . "::\$" . $property->name;
2797 self::$metadataParser->setTarget(Target::TARGET_PROPERTY);
2798
2799 foreach (self::$metadataParser->parse($propertyComment, $context) as $annotation) {
2800 if($annotation instanceof Enum) {
2801 $metadata['enum'][$property->name]['value'] = $annotation->value;
2802 $metadata['enum'][$property->name]['literal'] = ! empty($annotation->literal) ? $annotation->literal : $annotation->value;
2803 }
2804 }
2805 }
2806 }
2807
2808 // choose the first property as default property
2809 $metadata['default_property'] = reset($metadata['properties']);
2810 }
2811 }
2812
2813 self::$annotationMetadata[$name] = $metadata;
2814 }
2815
2816 /**
2817 * Annotations ::= Annotation {[ "*" ]* [Annotation]}*
2818 *
2819 * @return array
2820 */
2821 private function Annotations()
2822 {
2823 $annotations = array();
2824
2825 while (null !== $this->lexer->lookahead) {
2826 if (DocLexer::T_AT !== $this->lexer->lookahead['type']) {
2827 $this->lexer->moveNext();
2828 continue;
2829 }
2830
2831 // make sure the @ is preceded by non-catchable pattern
2832 if (null !== $this->lexer->token && $this->lexer->lookahead['position'] === $this->lexer->token['position'] + strlen($this->lexer->token['value'])) {
2833 $this->lexer->moveNext();
2834 continue;
2835 }
2836
2837 // make sure the @ is followed by either a namespace separator, or
2838 // an identifier token
2839 if ((null === $peek = $this->lexer->glimpse())
2840 || (DocLexer::T_NAMESPACE_SEPARATOR !== $peek['type'] && !in_array($peek['type'], self::$classIdentifiers, true))
2841 || $peek['position'] !== $this->lexer->lookahead['position'] + 1) {
2842 $this->lexer->moveNext();
2843 continue;
2844 }
2845
2846 $this->isNestedAnnotation = false;
2847 if (false !== $annot = $this->Annotation()) {
2848 $annotations[] = $annot;
2849 }
2850 }
2851
2852 return $annotations;
2853 }
2854
2855 /**
2856 * Annotation ::= "@" AnnotationName ["(" [Values] ")"]
2857 * AnnotationName ::= QualifiedName | SimpleName
2858 * QualifiedName ::= NameSpacePart "\" {NameSpacePart "\"}* SimpleName
2859 * NameSpacePart ::= identifier | null | false | true
2860 * SimpleName ::= identifier | null | false | true
2861 *
2862 * @throws AnnotationException
2863 * @return mixed False if it is not a valid annotation.
2864 */
2865 private function Annotation()
2866 {
2867 $this->match(DocLexer::T_AT);
2868
2869 // check if we have an annotation
2870 $name = $this->Identifier();
2871
2872 // only process names which are not fully qualified, yet
2873 // fully qualified names must start with a \
2874 $originalName = $name;
2875 if ('\\' !== $name[0]) {
2876 $alias = (false === $pos = strpos($name, '\\'))? $name : substr($name, 0, $pos);
2877
2878 $found = false;
2879 if ($this->namespaces) {
2880 foreach ($this->namespaces as $namespace) {
2881 if ($this->classExists($namespace.'\\'.$name)) {
2882 $name = $namespace.'\\'.$name;
2883 $found = true;
2884 break;
2885 }
2886 }
2887 } elseif (isset($this->imports[$loweredAlias = strtolower($alias)])) {
2888 if (false !== $pos) {
2889 $name = $this->imports[$loweredAlias].substr($name, $pos);
2890 } else {
2891 $name = $this->imports[$loweredAlias];
2892 }
2893 $found = true;
2894 } elseif (isset($this->imports['__NAMESPACE__']) && $this->classExists($this->imports['__NAMESPACE__'].'\\'.$name)) {
2895 $name = $this->imports['__NAMESPACE__'].'\\'.$name;
2896 $found = true;
2897 } elseif ($this->classExists($name)) {
2898 $found = true;
2899 }
2900
2901 if (!$found) {
2902 if ($this->ignoreNotImportedAnnotations || isset($this->ignoredAnnotationNames[$name])) {
2903 return false;
2904 }
2905
2906 throw AnnotationException::semanticalError(sprintf('The annotation "@%s" in %s was never imported. Did you maybe forget to add a "use" statement for this annotation?', $name, $this->context));
2907 }
2908 }
2909
2910 if (!$this->classExists($name)) {
2911 throw AnnotationException::semanticalError(sprintf('The annotation "@%s" in %s does not exist, or could not be auto-loaded.', $name, $this->context));
2912 }
2913
2914 // at this point, $name contains the fully qualified class name of the
2915 // annotation, and it is also guaranteed that this class exists, and
2916 // that it is loaded
2917
2918
2919 // collects the metadata annotation only if there is not yet
2920 if (!isset(self::$annotationMetadata[$name])) {
2921 $this->collectAnnotationMetadata($name);
2922 }
2923
2924 // verify that the class is really meant to be an annotation and not just any ordinary class
2925 if (self::$annotationMetadata[$name]['is_annotation'] === false) {
2926 if (isset($this->ignoredAnnotationNames[$originalName])) {
2927 return false;
2928 }
2929
2930 throw AnnotationException::semanticalError(sprintf('The class "%s" is not annotated with @Annotation. Are you sure this class can be used as annotation? If so, then you need to add @Annotation to the _class_ doc comment of "%s". If it is indeed no annotation, then you need to add @IgnoreAnnotation("%s") to the _class_ doc comment of %s.', $name, $name, $originalName, $this->context));
2931 }
2932
2933 //if target is nested annotation
2934 $target = $this->isNestedAnnotation ? Target::TARGET_ANNOTATION : $this->target;
2935
2936 // Next will be nested
2937 $this->isNestedAnnotation = true;
2938
2939 //if annotation does not support current target
2940 if (0 === (self::$annotationMetadata[$name]['targets'] & $target) && $target) {
2941 throw AnnotationException::semanticalError(
2942 sprintf('Annotation @%s is not allowed to be declared on %s. You may only use this annotation on these code elements: %s.',
2943 $originalName, $this->context, self::$annotationMetadata[$name]['targets_literal'])
2944 );
2945 }
2946
2947 $values = array();
2948 if ($this->lexer->isNextToken(DocLexer::T_OPEN_PARENTHESIS)) {
2949 $this->match(DocLexer::T_OPEN_PARENTHESIS);
2950
2951 if ( ! $this->lexer->isNextToken(DocLexer::T_CLOSE_PARENTHESIS)) {
2952 $values = $this->Values();
2953 }
2954
2955 $this->match(DocLexer::T_CLOSE_PARENTHESIS);
2956 }
2957
2958 if (isset(self::$annotationMetadata[$name]['enum'])) {
2959 // checks all declared attributes
2960 foreach (self::$annotationMetadata[$name]['enum'] as $property => $enum) {
2961 // checks if the attribute is a valid enumerator
2962 if (isset($values[$property]) && ! in_array($values[$property], $enum['value'])) {
2963 throw AnnotationException::enumeratorError($property, $name, $this->context, $enum['literal'], $values[$property]);
2964 }
2965 }
2966 }
2967
2968 // checks all declared attributes
2969 foreach (self::$annotationMetadata[$name]['attribute_types'] as $property => $type) {
2970 if ($property === self::$annotationMetadata[$name]['default_property']
2971 && !isset($values[$property]) && isset($values['value'])) {
2972 $property = 'value';
2973 }
2974
2975 // handle a not given attribute or null value
2976 if (!isset($values[$property])) {
2977 if ($type['required']) {
2978 throw AnnotationException::requiredError($property, $originalName, $this->context, 'a(n) '.$type['value']);
2979 }
2980
2981 continue;
2982 }
2983
2984 if ($type['type'] === 'array') {
2985 // handle the case of a single value
2986 if ( ! is_array($values[$property])) {
2987 $values[$property] = array($values[$property]);
2988 }
2989
2990 // checks if the attribute has array type declaration, such as "array<string>"
2991 if (isset($type['array_type'])) {
2992 foreach ($values[$property] as $item) {
2993 if (gettype($item) !== $type['array_type'] && !$item instanceof $type['array_type']) {
2994 throw AnnotationException::typeError($property, $originalName, $this->context, 'either a(n) '.$type['array_type'].', or an array of '.$type['array_type'].'s', $item);
2995 }
2996 }
2997 }
2998 } elseif (gettype($values[$property]) !== $type['type'] && !$values[$property] instanceof $type['type']) {
2999 throw AnnotationException::typeError($property, $originalName, $this->context, 'a(n) '.$type['value'], $values[$property]);
3000 }
3001 }
3002
3003 // check if the annotation expects values via the constructor,
3004 // or directly injected into public properties
3005 if (self::$annotationMetadata[$name]['has_constructor'] === true) {
3006 return new $name($values);
3007 }
3008
3009 $instance = new $name();
3010 foreach ($values as $property => $value) {
3011 if (!isset(self::$annotationMetadata[$name]['properties'][$property])) {
3012 if ('value' !== $property) {
3013 throw AnnotationException::creationError(sprintf('The annotation @%s declared on %s does not have a property named "%s". Available properties: %s', $originalName, $this->context, $property, implode(', ', self::$annotationMetadata[$name]['properties'])));
3014 }
3015
3016 // handle the case if the property has no annotations
3017 if (!$property = self::$annotationMetadata[$name]['default_property']) {
3018 throw AnnotationException::creationError(sprintf('The annotation @%s declared on %s does not accept any values, but got %s.', $originalName, $this->context, json_encode($values)));
3019 }
3020 }
3021
3022 $instance->{$property} = $value;
3023 }
3024
3025 return $instance;
3026 }
3027
3028 /**
3029 * Values ::= Array | Value {"," Value}*
3030 *
3031 * @return array
3032 */
3033 private function Values()
3034 {
3035 $values = array();
3036
3037 // Handle the case of a single array as value, i.e. @Foo({....})
3038 if ($this->lexer->isNextToken(DocLexer::T_OPEN_CURLY_BRACES)) {
3039 $values['value'] = $this->Value();
3040 return $values;
3041 }
3042
3043 $values[] = $this->Value();
3044
3045 while ($this->lexer->isNextToken(DocLexer::T_COMMA)) {
3046 $this->match(DocLexer::T_COMMA);
3047 $token = $this->lexer->lookahead;
3048 $value = $this->Value();
3049
3050 if ( ! is_object($value) && ! is_array($value)) {
3051 $this->syntaxError('Value', $token);
3052 }
3053
3054 $values[] = $value;
3055 }
3056
3057 foreach ($values as $k => $value) {
3058 if (is_object($value) && $value instanceof \stdClass) {
3059 $values[$value->name] = $value->value;
3060 } else if ( ! isset($values['value'])){
3061 $values['value'] = $value;
3062 } else {
3063 if ( ! is_array($values['value'])) {
3064 $values['value'] = array($values['value']);
3065 }
3066
3067 $values['value'][] = $value;
3068 }
3069
3070 unset($values[$k]);
3071 }
3072
3073 return $values;
3074 }
3075
3076 /**
3077 * Constant ::= integer | string | float | boolean
3078 *
3079 * @throws AnnotationException
3080 * @return mixed
3081 */
3082 private function Constant()
3083 {
3084 $identifier = $this->Identifier();
3085
3086 if (!defined($identifier) && false !== strpos($identifier, '::') && '\\' !== $identifier[0]) {
3087
3088 list($className, $const) = explode('::', $identifier);
3089 $alias = (false === $pos = strpos($className, '\\'))? $className : substr($className, 0, $pos);
3090
3091 $found = false;
3092 switch (true) {
3093 case !empty ($this->namespaces):
3094 foreach ($this->namespaces as $ns) {
3095 if (class_exists($ns.'\\'.$className) || interface_exists($ns.'\\'.$className)) {
3096 $className = $ns.'\\'.$className;
3097 $found = true;
3098 break;
3099 }
3100 }
3101 break;
3102
3103 case isset($this->imports[$loweredAlias = strtolower($alias)]):
3104 $found = true;
3105 if (false !== $pos) {
3106 $className = $this->imports[$loweredAlias].substr($className, $pos);
3107 } else {
3108 $className = $this->imports[$loweredAlias];
3109 }
3110 break;
3111
3112 default:
3113 if(isset($this->imports['__NAMESPACE__'])) {
3114 $ns = $this->imports['__NAMESPACE__'];
3115 if (class_exists($ns.'\\'.$className) || interface_exists($ns.'\\'.$className)) {
3116 $className = $ns.'\\'.$className;
3117 $found = true;
3118 }
3119 }
3120 break;
3121 }
3122
3123 if ($found) {
3124 $identifier = $className . '::' . $const;
3125 }
3126 }
3127
3128 if (!defined($identifier)) {
3129 throw AnnotationException::semanticalErrorConstants($identifier, $this->context);
3130 }
3131
3132 return constant($identifier);
3133 }
3134
3135 /**
3136 * Identifier ::= string
3137 *
3138 * @return string
3139 */
3140 private function Identifier()
3141 {
3142 // check if we have an annotation
3143 if ($this->lexer->isNextTokenAny(self::$classIdentifiers)) {
3144 $this->lexer->moveNext();
3145 $className = $this->lexer->token['value'];
3146 } else {
3147 $this->syntaxError('namespace separator or identifier');
3148 }
3149
3150 while ($this->lexer->lookahead['position'] === ($this->lexer->token['position'] + strlen($this->lexer->token['value']))
3151 && $this->lexer->isNextToken(DocLexer::T_NAMESPACE_SEPARATOR)) {
3152
3153 $this->match(DocLexer::T_NAMESPACE_SEPARATOR);
3154 $this->matchAny(self::$classIdentifiers);
3155 $className .= '\\' . $this->lexer->token['value'];
3156 }
3157
3158 return $className;
3159 }
3160
3161 /**
3162 * Value ::= PlainValue | FieldAssignment
3163 *
3164 * @return mixed
3165 */
3166 private function Value()
3167 {
3168 $peek = $this->lexer->glimpse();
3169
3170 if (DocLexer::T_EQUALS === $peek['type']) {
3171 return $this->FieldAssignment();
3172 }
3173
3174 return $this->PlainValue();
3175 }
3176
3177 /**
3178 * PlainValue ::= integer | string | float | boolean | Array | Annotation
3179 *
3180 * @return mixed
3181 */
3182 private function PlainValue()
3183 {
3184 if ($this->lexer->isNextToken(DocLexer::T_OPEN_CURLY_BRACES)) {
3185 return $this->Arrayx();
3186 }
3187
3188 if ($this->lexer->isNextToken(DocLexer::T_AT)) {
3189 return $this->Annotation();
3190 }
3191
3192 if ($this->lexer->isNextToken(DocLexer::T_IDENTIFIER)) {
3193 return $this->Constant();
3194 }
3195
3196 switch ($this->lexer->lookahead['type']) {
3197 case DocLexer::T_STRING:
3198 $this->match(DocLexer::T_STRING);
3199 return $this->lexer->token['value'];
3200
3201 case DocLexer::T_INTEGER:
3202 $this->match(DocLexer::T_INTEGER);
3203 return (int)$this->lexer->token['value'];
3204
3205 case DocLexer::T_FLOAT:
3206 $this->match(DocLexer::T_FLOAT);
3207 return (float)$this->lexer->token['value'];
3208
3209 case DocLexer::T_TRUE:
3210 $this->match(DocLexer::T_TRUE);
3211 return true;
3212
3213 case DocLexer::T_FALSE:
3214 $this->match(DocLexer::T_FALSE);
3215 return false;
3216
3217 case DocLexer::T_NULL:
3218 $this->match(DocLexer::T_NULL);
3219 return null;
3220
3221 default:
3222 $this->syntaxError('PlainValue');
3223 }
3224 }
3225
3226 /**
3227 * FieldAssignment ::= FieldName "=" PlainValue
3228 * FieldName ::= identifier
3229 *
3230 * @return array
3231 */
3232 private function FieldAssignment()
3233 {
3234 $this->match(DocLexer::T_IDENTIFIER);
3235 $fieldName = $this->lexer->token['value'];
3236
3237 $this->match(DocLexer::T_EQUALS);
3238
3239 $item = new \stdClass();
3240 $item->name = $fieldName;
3241 $item->value = $this->PlainValue();
3242
3243 return $item;
3244 }
3245
3246 /**
3247 * Array ::= "{" ArrayEntry {"," ArrayEntry}* [","] "}"
3248 *
3249 * @return array
3250 */
3251 private function Arrayx()
3252 {
3253 $array = $values = array();
3254
3255 $this->match(DocLexer::T_OPEN_CURLY_BRACES);
3256
3257 // If the array is empty, stop parsing and return.
3258 if ($this->lexer->isNextToken(DocLexer::T_CLOSE_CURLY_BRACES)) {
3259 $this->match(DocLexer::T_CLOSE_CURLY_BRACES);
3260
3261 return $array;
3262 }
3263
3264 $values[] = $this->ArrayEntry();
3265
3266 while ($this->lexer->isNextToken(DocLexer::T_COMMA)) {
3267 $this->match(DocLexer::T_COMMA);
3268
3269 // optional trailing comma
3270 if ($this->lexer->isNextToken(DocLexer::T_CLOSE_CURLY_BRACES)) {
3271 break;
3272 }
3273
3274 $values[] = $this->ArrayEntry();
3275 }
3276
3277 $this->match(DocLexer::T_CLOSE_CURLY_BRACES);
3278
3279 foreach ($values as $value) {
3280 list ($key, $val) = $value;
3281
3282 if ($key !== null) {
3283 $array[$key] = $val;
3284 } else {
3285 $array[] = $val;
3286 }
3287 }
3288
3289 return $array;
3290 }
3291
3292 /**
3293 * ArrayEntry ::= Value | KeyValuePair
3294 * KeyValuePair ::= Key ("=" | ":") PlainValue | Constant
3295 * Key ::= string | integer | Constant
3296 *
3297 * @return array
3298 */
3299 private function ArrayEntry()
3300 {
3301 $peek = $this->lexer->glimpse();
3302
3303 if (DocLexer::T_EQUALS === $peek['type']
3304 || DocLexer::T_COLON === $peek['type']) {
3305
3306 if ($this->lexer->isNextToken(DocLexer::T_IDENTIFIER)) {
3307 $key = $this->Constant();
3308 } else {
3309 $this->matchAny(array(DocLexer::T_INTEGER, DocLexer::T_STRING));
3310 $key = $this->lexer->token['value'];
3311 }
3312
3313 $this->matchAny(array(DocLexer::T_EQUALS, DocLexer::T_COLON));
3314
3315 return array($key, $this->PlainValue());
3316 }
3317
3318 return array(null, $this->Value());
3319 }
3320 }
3321 <?php
3322 /*
3323 * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
3324 * "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
3325 * LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR
3326 * A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT
3327 * OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
3328 * SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT
3329 * LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
3330 * DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
3331 * THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
3332 * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
3333 * OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
3334 *
3335 * This software consists of voluntary contributions made by many individuals
3336 * and is licensed under the MIT license. For more information, see
3337 * <http://www.doctrine-project.org>.
3338 */
3339
3340 namespace Doctrine\Common\Annotations\Annotation;
3341
3342 /**
3343 * Annotation that can be used to signal to the parser to ignore specific
3344 * annotations during the parsing process.
3345 *
3346 * @Annotation
3347 * @author Johannes M. Schmitt <schmittjoh@gmail.com>
3348 */
3349 final class IgnoreAnnotation
3350 {
3351 /**
3352 * @var array
3353 */
3354 public $names;
3355
3356 /**
3357 * Constructor
3358 *
3359 * @param array $values
3360 *
3361 * @throws \RuntimeException
3362 */
3363 public function __construct(array $values)
3364 {
3365 if (is_string($values['value'])) {
3366 $values['value'] = array($values['value']);
3367 }
3368 if (!is_array($values['value'])) {
3369 throw new \RuntimeException(sprintf('@IgnoreAnnotation expects either a string name, or an array of strings, but got %s.', json_encode($values['value'])));
3370 }
3371
3372 $this->names = $values['value'];
3373 }
3374 }
3375 <?php
3376
3377 /*
3378 * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
3379 * "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
3380 * LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR
3381 * A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT
3382 * OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
3383 * SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT
3384 * LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
3385 * DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
3386 * THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
3387 * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
3388 * OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
3389 *
3390 * This software consists of voluntary contributions made by many individuals
3391 * and is licensed under the MIT license. For more information, see
3392 * <http://www.doctrine-project.org>.
3393 */
3394
3395 namespace Doctrine\Common\Annotations\Annotation;
3396
3397 /**
3398 * Annotation that can be used to signal to the parser
3399 * to check the annotation target during the parsing process.
3400 *
3401 * @author Fabio B. Silva <fabio.bat.silva@gmail.com>
3402 *
3403 * @Annotation
3404 */
3405 final class Target
3406 {
3407 const TARGET_CLASS = 1;
3408 const TARGET_METHOD = 2;
3409 const TARGET_PROPERTY = 4;
3410 const TARGET_ANNOTATION = 8;
3411 const TARGET_ALL = 15;
3412
3413 /**
3414 * @var array
3415 */
3416 private static $map = array(
3417 'ALL' => self::TARGET_ALL,
3418 'CLASS' => self::TARGET_CLASS,
3419 'METHOD' => self::TARGET_METHOD,
3420 'PROPERTY' => self::TARGET_PROPERTY,
3421 'ANNOTATION' => self::TARGET_ANNOTATION,
3422 );
3423
3424 /**
3425 * @var array
3426 */
3427 public $value;
3428
3429 /**
3430 * Targets as bitmask.
3431 *
3432 * @var integer
3433 */
3434 public $targets;
3435
3436 /**
3437 * Literal target declaration.
3438 *
3439 * @var integer
3440 */
3441 public $literal;
3442
3443 /**
3444 * Annotation construct
3445 *
3446 * @param array $values
3447 *
3448 * @throws \InvalidArgumentException
3449 */
3450 public function __construct(array $values)
3451 {
3452 if (!isset($values['value'])){
3453 $values['value'] = null;
3454 }
3455 if (is_string($values['value'])){
3456 $values['value'] = array($values['value']);
3457 }
3458 if (!is_array($values['value'])){
3459 throw new \InvalidArgumentException(
3460 sprintf('@Target expects either a string value, or an array of strings, "%s" given.',
3461 is_object($values['value']) ? get_class($values['value']) : gettype($values['value'])
3462 )
3463 );
3464 }
3465
3466 $bitmask = 0;
3467 foreach ($values['value'] as $literal) {
3468 if(!isset(self::$map[$literal])){
3469 throw new \InvalidArgumentException(
3470 sprintf('Invalid Target "%s". Available targets: [%s]',
3471 $literal, implode(', ', array_keys(self::$map)))
3472 );
3473 }
3474 $bitmask += self::$map[$literal];
3475 }
3476
3477 $this->targets = $bitmask;
3478 $this->value = $values['value'];
3479 $this->literal = implode(', ', $this->value);
3480 }
3481 }
3482 <?php
3483
3484 /*
3485 * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
3486 * "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
3487 * LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR
3488 * A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT
3489 * OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
3490 * SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT
3491 * LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
3492 * DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
3493 * THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
3494 * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
3495 * OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
3496 *
3497 * This software consists of voluntary contributions made by many individuals
3498 * and is licensed under the MIT license. For more information, see
3499 * <http://www.doctrine-project.org>.
3500 */
3501
3502 namespace Doctrine\Common\Annotations\Annotation;
3503
3504 /**
3505 * Annotation that can be used to signal to the parser
3506 * to check the types of all declared attributes during the parsing process.
3507 *
3508 * @author Fabio B. Silva <fabio.bat.silva@gmail.com>
3509 *
3510 * @Annotation
3511 */
3512 final class Attributes
3513 {
3514 /**
3515 * @var array<Doctrine\Common\Annotations\Annotation\Attribute>
3516 */
3517 public $value;
3518 }
3519 <?php
3520
3521 /*
3522 * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
3523 * "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
3524 * LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR
3525 * A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT
3526 * OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
3527 * SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT
3528 * LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
3529 * DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
3530 * THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
3531 * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
3532 * OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
3533 *
3534 * This software consists of voluntary contributions made by many individuals
3535 * and is licensed under the MIT license. For more information, see
3536 * <http://www.doctrine-project.org>.
3537 */
3538
3539 namespace Doctrine\Common\Annotations\Annotation;
3540
3541 /**
3542 * Annotation that can be used to signal to the parser
3543 * to check the attribute type during the parsing process.
3544 *
3545 * @author Fabio B. Silva <fabio.bat.silva@gmail.com>
3546 *
3547 * @Annotation
3548 */
3549 final class Attribute
3550 {
3551 /**
3552 * @var string
3553 */
3554 public $name;
3555
3556 /**
3557 * @var string
3558 */
3559 public $type;
3560
3561 /**
3562 * @var boolean
3563 */
3564 public $required = false;
3565 }
3566 <?php
3567
3568 /*
3569 * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
3570 * "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
3571 * LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR
3572 * A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT
3573 * OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
3574 * SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT
3575 * LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
3576 * DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
3577 * THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
3578 * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
3579 * OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
3580 *
3581 * This software consists of voluntary contributions made by many individuals
3582 * and is licensed under the MIT license. For more information, see
3583 * <http://www.doctrine-project.org>.
3584 */
3585
3586 namespace Doctrine\Common\Annotations\Annotation;
3587
3588 /**
3589 * Annotation that can be used to signal to the parser
3590 * to check if that attribute is required during the parsing process.
3591 *
3592 * @author Fabio B. Silva <fabio.bat.silva@gmail.com>
3593 *
3594 * @Annotation
3595 */
3596 final class Required
3597 {
3598 }
3599 <?php
3600
3601 /*
3602 * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
3603 * "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
3604 * LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR
3605 * A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT
3606 * OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
3607 * SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT
3608 * LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
3609 * DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
3610 * THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
3611 * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
3612 * OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
3613 *
3614 * This software consists of voluntary contributions made by many individuals
3615 * and is licensed under the MIT license. For more information, see
3616 * <http://www.doctrine-project.org>.
3617 */
3618
3619 namespace Doctrine\Common\Annotations\Annotation;
3620
3621 /**
3622 * Annotation that can be used to signal to the parser
3623 * to check the available values during the parsing process.
3624 *
3625 * @since 2.4
3626 * @author Fabio B. Silva <fabio.bat.silva@gmail.com>
3627 *
3628 * @Annotation
3629 * @Attributes({
3630 * @Attribute("value", required = true, type = "array"),
3631 * @Attribute("literal", required = false, type = "array")
3632 * })
3633 */
3634 final class Enum
3635 {
3636 /**
3637 * @var array
3638 */
3639 public $value;
3640
3641 /**
3642 * Literal target declaration.
3643 *
3644 * @var array
3645 */
3646 public $literal;
3647
3648 /**
3649 * Annotation construct
3650 *
3651 * @param array $values
3652 *
3653 * @throws \InvalidArgumentException
3654 */
3655 public function __construct(array $values)
3656 {
3657 if ( ! isset($values['literal'])) {
3658 $values['literal'] = array();
3659 }
3660
3661 foreach ($values['value'] as $var) {
3662 if( ! is_scalar($var)) {
3663 throw new \InvalidArgumentException(sprintf(
3664 '@Enum supports only scalar values "%s" given.',
3665 is_object($var) ? get_class($var) : gettype($var)
3666 ));
3667 }
3668 }
3669
3670 foreach ($values['literal'] as $key => $var) {
3671 if( ! in_array($key, $values['value'])) {
3672 throw new \InvalidArgumentException(sprintf(
3673 'Undefined enumerator value "%s" for literal "%s".',
3674 $key , $var
3675 ));
3676 }
3677 }
3678
3679 $this->value = $values['value'];
3680 $this->literal = $values['literal'];
3681 }
3682
3683 }<?php
3684 /*
3685 * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
3686 * "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
3687 * LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR
3688 * A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT
3689 * OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
3690 * SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT
3691 * LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
3692 * DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
3693 * THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
3694 * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
3695 * OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
3696 *
3697 * This software consists of voluntary contributions made by many individuals
3698 * and is licensed under the MIT license. For more information, see
3699 * <http://www.doctrine-project.org>.
3700 */
3701
3702 namespace Doctrine\Common\Annotations;
3703
3704 /**
3705 * Description of AnnotationException
3706 *
3707 * @since 2.0
3708 * @author Benjamin Eberlei <kontakt@beberlei.de>
3709 * @author Guilherme Blanco <guilhermeblanco@hotmail.com>
3710 * @author Jonathan Wage <jonwage@gmail.com>
3711 * @author Roman Borschel <roman@code-factory.org>
3712 */
3713 class AnnotationException extends \Exception
3714 {
3715 /**
3716 * Creates a new AnnotationException describing a Syntax error.
3717 *
3718 * @param string $message Exception message
3719 * @return AnnotationException
3720 */
3721 public static function syntaxError($message)
3722 {
3723 return new self('[Syntax Error] ' . $message);
3724 }
3725
3726 /**
3727 * Creates a new AnnotationException describing a Semantical error.
3728 *
3729 * @param string $message Exception message
3730 * @return AnnotationException
3731 */
3732 public static function semanticalError($message)
3733 {
3734 return new self('[Semantical Error] ' . $message);
3735 }
3736
3737 /**
3738 * Creates a new AnnotationException describing a constant semantical error.
3739 *
3740 * @since 2.3
3741 * @param string $identifier
3742 * @param string $context
3743 * @return AnnotationException
3744 */
3745 public static function semanticalErrorConstants($identifier, $context = null)
3746 {
3747 return self::semanticalError(sprintf(
3748 "Couldn't find constant %s%s", $identifier,
3749 $context ? ", $context." : "."
3750 ));
3751 }
3752
3753 /**
3754 * Creates a new AnnotationException describing an error which occurred during
3755 * the creation of the annotation.
3756 *
3757 * @since 2.2
3758 * @param string $message
3759 * @return AnnotationException
3760 */
3761 public static function creationError($message)
3762 {
3763 return new self('[Creation Error] ' . $message);
3764 }
3765
3766 /**
3767 * Creates a new AnnotationException describing an type error of an attribute.
3768 *
3769 * @since 2.2
3770 * @param string $attributeName
3771 * @param string $annotationName
3772 * @param string $context
3773 * @param string $expected
3774 * @param mixed $actual
3775 * @return AnnotationException
3776 */
3777 public static function typeError($attributeName, $annotationName, $context, $expected, $actual)
3778 {
3779 return new self(sprintf(
3780 '[Type Error] Attribute "%s" of @%s declared on %s expects %s, but got %s.',
3781 $attributeName,
3782 $annotationName,
3783 $context,
3784 $expected,
3785 is_object($actual) ? 'an instance of '.get_class($actual) : gettype($actual)
3786 ));
3787 }
3788
3789 /**
3790 * Creates a new AnnotationException describing an required error of an attribute.
3791 *
3792 * @since 2.2
3793 * @param string $attributeName
3794 * @param string $annotationName
3795 * @param string $context
3796 * @param string $expected
3797 * @return AnnotationException
3798 */
3799 public static function requiredError($attributeName, $annotationName, $context, $expected)
3800 {
3801 return new self(sprintf(
3802 '[Type Error] Attribute "%s" of @%s declared on %s expects %s. This value should not be null.',
3803 $attributeName,
3804 $annotationName,
3805 $context,
3806 $expected
3807 ));
3808 }
3809
3810 /**
3811 * Creates a new AnnotationException describing a invalid enummerator.
3812 *
3813 * @since 2.4
3814 * @param string $attributeName
3815 * @param string $annotationName
3816 * @param string $context
3817 * @param array $available
3818 * @param mixed $given
3819 * @return AnnotationException
3820 */
3821 public static function enumeratorError($attributeName, $annotationName, $context, $available, $given)
3822 {
3823 throw new self(sprintf(
3824 '[Enum Error] Attribute "%s" of @%s declared on %s accept only [%s], but got %s.',
3825 $attributeName,
3826 $annotationName,
3827 $context,
3828 implode(', ', $available),
3829 is_object($given) ? get_class($given) : $given
3830 ));
3831 }
3832
3833 /**
3834 * @return AnnotationException
3835 */
3836 public static function optimizerPlusSaveComments()
3837 {
3838 throw new self("You have to enable opcache.save_comments=1 or zend_optimizerplus.save_comments=1.");
3839 }
3840 }
3841 <?php
3842
3843 namespace Doctrine\Tests\Common\Annotations;
3844
3845 use Doctrine\Common\Annotations\Annotation\IgnorePhpDoc;
3846 use Doctrine\Common\Annotations\Annotation\IgnoreAnnotation;
3847 use Doctrine\Common\Annotations\DocParser;
3848 use Doctrine\Common\Annotations\AnnotationRegistry;
3849 use Doctrine\Common\Annotations\Annotation\Target;
3850 use Doctrine\Tests\Common\Annotations\Fixtures\AnnotationWithConstants;
3851 use Doctrine\Tests\Common\Annotations\Fixtures\ClassWithConstants;
3852 use Doctrine\Tests\Common\Annotations\Fixtures\IntefaceWithConstants;
3853
3854 class DocParserTest extends \PHPUnit_Framework_TestCase
3855 {
3856 public function testNestedArraysWithNestedAnnotation()
3857 {
3858 $parser = $this->createTestParser();
3859
3860 // Nested arrays with nested annotations
3861 $result = $parser->parse('@Name(foo={1,2, {"key"=@Name}})');
3862 $annot = $result[0];
3863
3864 $this->assertTrue($annot instanceof Name);
3865 $this->assertNull($annot->value);
3866 $this->assertEquals(3, count($annot->foo));
3867 $this->assertEquals(1, $annot->foo[0]);
3868 $this->assertEquals(2, $annot->foo[1]);
3869 $this->assertTrue(is_array($annot->foo[2]));
3870
3871 $nestedArray = $annot->foo[2];
3872 $this->assertTrue(isset($nestedArray['key']));
3873 $this->assertTrue($nestedArray['key'] instanceof Name);
3874 }
3875
3876 public function testBasicAnnotations()
3877 {
3878 $parser = $this->createTestParser();
3879
3880 // Marker annotation
3881 $result = $parser->parse("@Name");
3882 $annot = $result[0];
3883 $this->assertTrue($annot instanceof Name);
3884 $this->assertNull($annot->value);
3885 $this->assertNull($annot->foo);
3886
3887 // Associative arrays
3888 $result = $parser->parse('@Name(foo={"key1" = "value1"})');
3889 $annot = $result[0];
3890 $this->assertNull($annot->value);
3891 $this->assertTrue(is_array($annot->foo));
3892 $this->assertTrue(isset($annot->foo['key1']));
3893
3894 // Numerical arrays
3895 $result = $parser->parse('@Name({2="foo", 4="bar"})');
3896 $annot = $result[0];
3897 $this->assertTrue(is_array($annot->value));
3898 $this->assertEquals('foo', $annot->value[2]);
3899 $this->assertEquals('bar', $annot->value[4]);
3900 $this->assertFalse(isset($annot->value[0]));
3901 $this->assertFalse(isset($annot->value[1]));
3902 $this->assertFalse(isset($annot->value[3]));
3903
3904 // Multiple values
3905 $result = $parser->parse('@Name(@Name, @Name)');
3906 $annot = $result[0];
3907
3908 $this->assertTrue($annot instanceof Name);
3909 $this->assertTrue(is_array($annot->value));
3910 $this->assertTrue($annot->value[0] instanceof Name);
3911 $this->assertTrue($annot->value[1] instanceof Name);
3912
3913 // Multiple types as values
3914 $result = $parser->parse('@Name(foo="Bar", @Name, {"key1"="value1", "key2"="value2"})');
3915 $annot = $result[0];
3916
3917 $this->assertTrue($annot instanceof Name);
3918 $this->assertTrue(is_array($annot->value));
3919 $this->assertTrue($annot->value[0] instanceof Name);
3920 $this->assertTrue(is_array($annot->value[1]));
3921 $this->assertEquals('value1', $annot->value[1]['key1']);
3922 $this->assertEquals('value2', $annot->value[1]['key2']);
3923
3924 // Complete docblock
3925 $docblock = <<<DOCBLOCK
3926 /**
3927 * Some nifty class.
3928 *
3929 * @author Mr.X
3930 * @Name(foo="bar")
3931 */
3932 DOCBLOCK;
3933
3934 $result = $parser->parse($docblock);
3935 $this->assertEquals(1, count($result));
3936 $annot = $result[0];
3937 $this->assertTrue($annot instanceof Name);
3938 $this->assertEquals("bar", $annot->foo);
3939 $this->assertNull($annot->value);
3940 }
3941
3942 public function testNamespacedAnnotations()
3943 {
3944 $parser = new DocParser;
3945 $parser->setIgnoreNotImportedAnnotations(true);
3946
3947 $docblock = <<<DOCBLOCK
3948 /**
3949 * Some nifty class.
3950 *
3951 * @package foo
3952 * @subpackage bar
3953 * @author Mr.X <mr@x.com>
3954 * @Doctrine\Tests\Common\Annotations\Name(foo="bar")
3955 * @ignore
3956 */
3957 DOCBLOCK;
3958
3959 $result = $parser->parse($docblock);
3960 $this->assertEquals(1, count($result));
3961 $annot = $result[0];
3962 $this->assertTrue($annot instanceof Name);
3963 $this->assertEquals("bar", $annot->foo);
3964 }
3965
3966 /**
3967 * @group debug
3968 */
3969 public function testTypicalMethodDocBlock()
3970 {
3971 $parser = $this->createTestParser();
3972
3973 $docblock = <<<DOCBLOCK
3974 /**
3975 * Some nifty method.
3976 *
3977 * @since 2.0
3978 * @Doctrine\Tests\Common\Annotations\Name(foo="bar")
3979 * @param string \$foo This is foo.
3980 * @param mixed \$bar This is bar.
3981 * @return string Foo and bar.
3982 * @This is irrelevant
3983 * @Marker
3984 */
3985 DOCBLOCK;
3986
3987 $result = $parser->parse($docblock);
3988 $this->assertEquals(2, count($result));
3989 $this->assertTrue(isset($result[0]));
3990 $this->assertTrue(isset($result[1]));
3991 $annot = $result[0];
3992 $this->assertTrue($annot instanceof Name);
3993 $this->assertEquals("bar", $annot->foo);
3994 $marker = $result[1];
3995 $this->assertTrue($marker instanceof Marker);
3996 }
3997
3998
3999 public function testAnnotationWithoutConstructor()
4000 {
4001 $parser = $this->createTestParser();
4002
4003
4004 $docblock = <<<DOCBLOCK
4005 /**
4006 * @SomeAnnotationClassNameWithoutConstructor("Some data")
4007 */
4008 DOCBLOCK;
4009
4010 $result = $parser->parse($docblock);
4011 $this->assertEquals(count($result), 1);
4012 $annot = $result[0];
4013
4014 $this->assertNotNull($annot);
4015 $this->assertTrue($annot instanceof SomeAnnotationClassNameWithoutConstructor);
4016
4017 $this->assertNull($annot->name);
4018 $this->assertNotNull($annot->data);
4019 $this->assertEquals($annot->data, "Some data");
4020
4021
4022
4023
4024 $docblock = <<<DOCBLOCK
4025 /**
4026 * @SomeAnnotationClassNameWithoutConstructor(name="Some Name", data = "Some data")
4027 */
4028 DOCBLOCK;
4029
4030
4031 $result = $parser->parse($docblock);
4032 $this->assertEquals(count($result), 1);
4033 $annot = $result[0];
4034
4035 $this->assertNotNull($annot);
4036 $this->assertTrue($annot instanceof SomeAnnotationClassNameWithoutConstructor);
4037
4038 $this->assertEquals($annot->name, "Some Name");
4039 $this->assertEquals($annot->data, "Some data");
4040
4041
4042
4043
4044 $docblock = <<<DOCBLOCK
4045 /**
4046 * @SomeAnnotationClassNameWithoutConstructor(data = "Some data")
4047 */
4048 DOCBLOCK;
4049
4050 $result = $parser->parse($docblock);
4051 $this->assertEquals(count($result), 1);
4052 $annot = $result[0];
4053
4054 $this->assertEquals($annot->data, "Some data");
4055 $this->assertNull($annot->name);
4056
4057
4058 $docblock = <<<DOCBLOCK
4059 /**
4060 * @SomeAnnotationClassNameWithoutConstructor(name = "Some name")
4061 */
4062 DOCBLOCK;
4063
4064 $result = $parser->parse($docblock);
4065 $this->assertEquals(count($result), 1);
4066 $annot = $result[0];
4067
4068 $this->assertEquals($annot->name, "Some name");
4069 $this->assertNull($annot->data);
4070
4071 $docblock = <<<DOCBLOCK
4072 /**
4073 * @SomeAnnotationClassNameWithoutConstructor("Some data")
4074 */
4075 DOCBLOCK;
4076
4077 $result = $parser->parse($docblock);
4078 $this->assertEquals(count($result), 1);
4079 $annot = $result[0];
4080
4081 $this->assertEquals($annot->data, "Some data");
4082 $this->assertNull($annot->name);
4083
4084
4085
4086 $docblock = <<<DOCBLOCK
4087 /**
4088 * @SomeAnnotationClassNameWithoutConstructor("Some data",name = "Some name")
4089 */
4090 DOCBLOCK;
4091
4092 $result = $parser->parse($docblock);
4093 $this->assertEquals(count($result), 1);
4094 $annot = $result[0];
4095
4096 $this->assertEquals($annot->name, "Some name");
4097 $this->assertEquals($annot->data, "Some data");
4098
4099
4100 $docblock = <<<DOCBLOCK
4101 /**
4102 * @SomeAnnotationWithConstructorWithoutParams(name = "Some name")
4103 */
4104 DOCBLOCK;
4105
4106 $result = $parser->parse($docblock);
4107 $this->assertEquals(count($result), 1);
4108 $annot = $result[0];
4109
4110 $this->assertEquals($annot->name, "Some name");
4111 $this->assertEquals($annot->data, "Some data");
4112
4113 $docblock = <<<DOCBLOCK
4114 /**
4115 * @SomeAnnotationClassNameWithoutConstructorAndProperties()
4116 */
4117 DOCBLOCK;
4118
4119 $result = $parser->parse($docblock);
4120 $this->assertEquals(count($result), 1);
4121 $this->assertTrue($result[0] instanceof SomeAnnotationClassNameWithoutConstructorAndProperties);
4122 }
4123
4124 public function testAnnotationTarget()
4125 {
4126
4127 $parser = new DocParser;
4128 $parser->setImports(array(
4129 '__NAMESPACE__' => 'Doctrine\Tests\Common\Annotations\Fixtures',
4130 ));
4131 $class = new \ReflectionClass('Doctrine\Tests\Common\Annotations\Fixtures\ClassWithValidAnnotationTarget');
4132
4133
4134 $context = 'class ' . $class->getName();
4135 $docComment = $class->getDocComment();
4136
4137 $parser->setTarget(Target::TARGET_CLASS);
4138 $this->assertNotNull($parser->parse($docComment,$context));
4139
4140
4141 $property = $class->getProperty('foo');
4142 $docComment = $property->getDocComment();
4143 $context = 'property ' . $class->getName() . "::\$" . $property->getName();
4144
4145 $parser->setTarget(Target::TARGET_PROPERTY);
4146 $this->assertNotNull($parser->parse($docComment,$context));
4147
4148
4149
4150 $method = $class->getMethod('someFunction');
4151 $docComment = $property->getDocComment();
4152 $context = 'method ' . $class->getName() . '::' . $method->getName() . '()';
4153
4154 $parser->setTarget(Target::TARGET_METHOD);
4155 $this->assertNotNull($parser->parse($docComment,$context));
4156
4157
4158 try {
4159 $class = new \ReflectionClass('Doctrine\Tests\Common\Annotations\Fixtures\ClassWithInvalidAnnotationTargetAtClass');
4160 $context = 'class ' . $class->getName();
4161 $docComment = $class->getDocComment();
4162
4163 $parser->setTarget(Target::TARGET_CLASS);
4164 $parser->parse($class->getDocComment(),$context);
4165
4166 $this->fail();
4167 } catch (\Doctrine\Common\Annotations\AnnotationException $exc) {
4168 $this->assertNotNull($exc->getMessage());
4169 }
4170
4171
4172 try {
4173
4174 $class = new \ReflectionClass('Doctrine\Tests\Common\Annotations\Fixtures\ClassWithInvalidAnnotationTargetAtMethod');
4175 $method = $class->getMethod('functionName');
4176 $docComment = $method->getDocComment();
4177 $context = 'method ' . $class->getName() . '::' . $method->getName() . '()';
4178
4179 $parser->setTarget(Target::TARGET_METHOD);
4180 $parser->parse($docComment,$context);
4181
4182 $this->fail();
4183 } catch (\Doctrine\Common\Annotations\AnnotationException $exc) {
4184 $this->assertNotNull($exc->getMessage());
4185 }
4186
4187
4188 try {
4189 $class = new \ReflectionClass('Doctrine\Tests\Common\Annotations\Fixtures\ClassWithInvalidAnnotationTargetAtProperty');
4190 $property = $class->getProperty('foo');
4191 $docComment = $property->getDocComment();
4192 $context = 'property ' . $class->getName() . "::\$" . $property->getName();
4193
4194 $parser->setTarget(Target::TARGET_PROPERTY);
4195 $parser->parse($docComment,$context);
4196
4197 $this->fail();
4198 } catch (\Doctrine\Common\Annotations\AnnotationException $exc) {
4199 $this->assertNotNull($exc->getMessage());
4200 }
4201
4202 }
4203
4204 public function getAnnotationVarTypeProviderValid()
4205 {
4206 //({attribute name}, {attribute value})
4207 return array(
4208 // mixed type
4209 array('mixed', '"String Value"'),
4210 array('mixed', 'true'),
4211 array('mixed', 'false'),
4212 array('mixed', '1'),
4213 array('mixed', '1.2'),
4214 array('mixed', '@Doctrine\Tests\Common\Annotations\Fixtures\AnnotationTargetAll'),
4215
4216 // boolean type
4217 array('boolean', 'true'),
4218 array('boolean', 'false'),
4219
4220 // alias for internal type boolean
4221 array('bool', 'true'),
4222 array('bool', 'false'),
4223
4224 // integer type
4225 array('integer', '0'),
4226 array('integer', '1'),
4227 array('integer', '123456789'),
4228 array('integer', '9223372036854775807'),
4229
4230 // alias for internal type double
4231 array('float', '0.1'),
4232 array('float', '1.2'),
4233 array('float', '123.456'),
4234
4235 // string type
4236 array('string', '"String Value"'),
4237 array('string', '"true"'),
4238 array('string', '"123"'),
4239
4240 // array type
4241 array('array', '{@AnnotationExtendsAnnotationTargetAll}'),
4242 array('array', '{@AnnotationExtendsAnnotationTargetAll,@AnnotationExtendsAnnotationTargetAll}'),
4243
4244 array('arrayOfIntegers', '1'),
4245 array('arrayOfIntegers', '{1}'),
4246 array('arrayOfIntegers', '{1,2,3,4}'),
4247 array('arrayOfAnnotations', '@AnnotationExtendsAnnotationTargetAll'),
4248 array('arrayOfAnnotations', '{@Doctrine\Tests\Common\Annotations\Fixtures\AnnotationTargetAll}'),
4249 array('arrayOfAnnotations', '{@AnnotationExtendsAnnotationTargetAll, @Doctrine\Tests\Common\Annotations\Fixtures\AnnotationTargetAll}'),
4250
4251 // annotation instance
4252 array('annotation', '@Doctrine\Tests\Common\Annotations\Fixtures\AnnotationTargetAll'),
4253 array('annotation', '@AnnotationExtendsAnnotationTargetAll'),
4254 );
4255 }
4256
4257 public function getAnnotationVarTypeProviderInvalid()
4258 {
4259 //({attribute name}, {type declared type}, {attribute value} , {given type or class})
4260 return array(
4261 // boolean type
4262 array('boolean','boolean','1','integer'),
4263 array('boolean','boolean','1.2','double'),
4264 array('boolean','boolean','"str"','string'),
4265 array('boolean','boolean','{1,2,3}','array'),
4266 array('boolean','boolean','@Name', 'an instance of Doctrine\Tests\Common\Annotations\Name'),
4267
4268 // alias for internal type boolean
4269 array('bool','bool', '1','integer'),
4270 array('bool','bool', '1.2','double'),
4271 array('bool','bool', '"str"','string'),
4272 array('bool','bool', '{"str"}','array'),
4273
4274 // integer type
4275 array('integer','integer', 'true','boolean'),
4276 array('integer','integer', 'false','boolean'),
4277 array('integer','integer', '1.2','double'),
4278 array('integer','integer', '"str"','string'),
4279 array('integer','integer', '{"str"}','array'),
4280 array('integer','integer', '{1,2,3,4}','array'),
4281
4282 // alias for internal type double
4283 array('float','float', 'true','boolean'),
4284 array('float','float', 'false','boolean'),
4285 array('float','float', '123','integer'),
4286 array('float','float', '"str"','string'),
4287 array('float','float', '{"str"}','array'),
4288 array('float','float', '{12.34}','array'),
4289 array('float','float', '{1,2,3}','array'),
4290
4291 // string type
4292 array('string','string', 'true','boolean'),
4293 array('string','string', 'false','boolean'),
4294 array('string','string', '12','integer'),
4295 array('string','string', '1.2','double'),
4296 array('string','string', '{"str"}','array'),
4297 array('string','string', '{1,2,3,4}','array'),
4298
4299 // annotation instance
4300 array('annotation','Doctrine\Tests\Common\Annotations\Fixtures\AnnotationTargetAll', 'true','boolean'),
4301 array('annotation','Doctrine\Tests\Common\Annotations\Fixtures\AnnotationTargetAll', 'false','boolean'),
4302 array('annotation','Doctrine\Tests\Common\Annotations\Fixtures\AnnotationTargetAll', '12','integer'),
4303 array('annotation','Doctrine\Tests\Common\Annotations\Fixtures\AnnotationTargetAll', '1.2','double'),
4304 array('annotation','Doctrine\Tests\Common\Annotations\Fixtures\AnnotationTargetAll', '{"str"}','array'),
4305 array('annotation','Doctrine\Tests\Common\Annotations\Fixtures\AnnotationTargetAll', '{1,2,3,4}','array'),
4306 array('annotation','Doctrine\Tests\Common\Annotations\Fixtures\AnnotationTargetAll', '@Name','an instance of Doctrine\Tests\Common\Annotations\Name'),
4307 );
4308 }
4309
4310 public function getAnnotationVarTypeArrayProviderInvalid()
4311 {
4312 //({attribute name}, {type declared type}, {attribute value} , {given type or class})
4313 return array(
4314 array('arrayOfIntegers','integer', 'true','boolean'),
4315 array('arrayOfIntegers','integer', 'false','boolean'),
4316 array('arrayOfIntegers','integer', '{true,true}','boolean'),
4317 array('arrayOfIntegers','integer', '{1,true}','boolean'),
4318 array('arrayOfIntegers','integer', '{1,2,1.2}','double'),
4319 array('arrayOfIntegers','integer', '{1,2,"str"}','string'),
4320
4321
4322 array('arrayOfAnnotations','Doctrine\Tests\Common\Annotations\Fixtures\AnnotationTargetAll', 'true','boolean'),
4323 array('arrayOfAnnotations','Doctrine\Tests\Common\Annotations\Fixtures\AnnotationTargetAll', 'false','boolean'),
4324 array('arrayOfAnnotations','Doctrine\Tests\Common\Annotations\Fixtures\AnnotationTargetAll', '{@Doctrine\Tests\Common\Annotations\Fixtures\AnnotationTargetAll,true}','boolean'),
4325 array('arrayOfAnnotations','Doctrine\Tests\Common\Annotations\Fixtures\AnnotationTargetAll', '{@Doctrine\Tests\Common\Annotations\Fixtures\AnnotationTargetAll,true}','boolean'),
4326 array('arrayOfAnnotations','Doctrine\Tests\Common\Annotations\Fixtures\AnnotationTargetAll', '{@Doctrine\Tests\Common\Annotations\Fixtures\AnnotationTargetAll,1.2}','double'),
4327 array('arrayOfAnnotations','Doctrine\Tests\Common\Annotations\Fixtures\AnnotationTargetAll', '{@Doctrine\Tests\Common\Annotations\Fixtures\AnnotationTargetAll,@AnnotationExtendsAnnotationTargetAll,"str"}','string'),
4328 );
4329 }
4330
4331 /**
4332 * @dataProvider getAnnotationVarTypeProviderValid
4333 */
4334 public function testAnnotationWithVarType($attribute, $value)
4335 {
4336 $parser = $this->createTestParser();
4337 $context = 'property SomeClassName::$invalidProperty.';
4338 $docblock = sprintf('@Doctrine\Tests\Common\Annotations\Fixtures\AnnotationWithVarType(%s = %s)',$attribute, $value);
4339 $parser->setTarget(Target::TARGET_PROPERTY);
4340
4341 $result = $parser->parse($docblock, $context);
4342
4343 $this->assertTrue(sizeof($result) === 1);
4344 $this->assertInstanceOf('Doctrine\Tests\Common\Annotations\Fixtures\AnnotationWithVarType', $result[0]);
4345 $this->assertNotNull($result[0]->$attribute);
4346 }
4347
4348 /**
4349 * @dataProvider getAnnotationVarTypeProviderInvalid
4350 */
4351 public function testAnnotationWithVarTypeError($attribute,$type,$value,$given)
4352 {
4353 $parser = $this->createTestParser();
4354 $context = 'property SomeClassName::invalidProperty.';
4355 $docblock = sprintf('@Doctrine\Tests\Common\Annotations\Fixtures\AnnotationWithVarType(%s = %s)',$attribute, $value);
4356 $parser->setTarget(Target::TARGET_PROPERTY);
4357
4358 try {
4359 $parser->parse($docblock, $context);
4360 $this->fail();
4361 } catch (\Doctrine\Common\Annotations\AnnotationException $exc) {
4362 $this->assertContains("[Type Error] Attribute \"$attribute\" of @Doctrine\Tests\Common\Annotations\Fixtures\AnnotationWithVarType declared on property SomeClassName::invalidProperty. expects a(n) $type, but got $given.", $exc->getMessage());
4363 }
4364 }
4365
4366
4367 /**
4368 * @dataProvider getAnnotationVarTypeArrayProviderInvalid
4369 */
4370 public function testAnnotationWithVarTypeArrayError($attribute,$type,$value,$given)
4371 {
4372 $parser = $this->createTestParser();
4373 $context = 'property SomeClassName::invalidProperty.';
4374 $docblock = sprintf('@Doctrine\Tests\Common\Annotations\Fixtures\AnnotationWithVarType(%s = %s)',$attribute, $value);
4375 $parser->setTarget(Target::TARGET_PROPERTY);
4376
4377 try {
4378 $parser->parse($docblock, $context);
4379 $this->fail();
4380 } catch (\Doctrine\Common\Annotations\AnnotationException $exc) {
4381 $this->assertContains("[Type Error] Attribute \"$attribute\" of @Doctrine\Tests\Common\Annotations\Fixtures\AnnotationWithVarType declared on property SomeClassName::invalidProperty. expects either a(n) $type, or an array of {$type}s, but got $given.", $exc->getMessage());
4382 }
4383 }
4384
4385 /**
4386 * @dataProvider getAnnotationVarTypeProviderValid
4387 */
4388 public function testAnnotationWithAttributes($attribute, $value)
4389 {
4390 $parser = $this->createTestParser();
4391 $context = 'property SomeClassName::$invalidProperty.';
4392 $docblock = sprintf('@Doctrine\Tests\Common\Annotations\Fixtures\AnnotationWithAttributes(%s = %s)',$attribute, $value);
4393 $parser->setTarget(Target::TARGET_PROPERTY);
4394
4395 $result = $parser->parse($docblock, $context);
4396
4397 $this->assertTrue(sizeof($result) === 1);
4398 $this->assertInstanceOf('Doctrine\Tests\Common\Annotations\Fixtures\AnnotationWithAttributes', $result[0]);
4399 $getter = "get".ucfirst($attribute);
4400 $this->assertNotNull($result[0]->$getter());
4401 }
4402
4403 /**
4404 * @dataProvider getAnnotationVarTypeProviderInvalid
4405 */
4406 public function testAnnotationWithAttributesError($attribute,$type,$value,$given)
4407 {
4408 $parser = $this->createTestParser();
4409 $context = 'property SomeClassName::invalidProperty.';
4410 $docblock = sprintf('@Doctrine\Tests\Common\Annotations\Fixtures\AnnotationWithAttributes(%s = %s)',$attribute, $value);
4411 $parser->setTarget(Target::TARGET_PROPERTY);
4412
4413 try {
4414 $parser->parse($docblock, $context);
4415 $this->fail();
4416 } catch (\Doctrine\Common\Annotations\AnnotationException $exc) {
4417 $this->assertContains("[Type Error] Attribute \"$attribute\" of @Doctrine\Tests\Common\Annotations\Fixtures\AnnotationWithAttributes declared on property SomeClassName::invalidProperty. expects a(n) $type, but got $given.", $exc->getMessage());
4418 }
4419 }
4420
4421
4422 /**
4423 * @dataProvider getAnnotationVarTypeArrayProviderInvalid
4424 */
4425 public function testAnnotationWithAttributesWithVarTypeArrayError($attribute,$type,$value,$given)
4426 {
4427 $parser = $this->createTestParser();
4428 $context = 'property SomeClassName::invalidProperty.';
4429 $docblock = sprintf('@Doctrine\Tests\Common\Annotations\Fixtures\AnnotationWithAttributes(%s = %s)',$attribute, $value);
4430 $parser->setTarget(Target::TARGET_PROPERTY);
4431
4432 try {
4433 $parser->parse($docblock, $context);
4434 $this->fail();
4435 } catch (\Doctrine\Common\Annotations\AnnotationException $exc) {
4436 $this->assertContains("[Type Error] Attribute \"$attribute\" of @Doctrine\Tests\Common\Annotations\Fixtures\AnnotationWithAttributes declared on property SomeClassName::invalidProperty. expects either a(n) $type, or an array of {$type}s, but got $given.", $exc->getMessage());
4437 }
4438 }
4439
4440 public function testAnnotationWithRequiredAttributes()
4441 {
4442 $parser = $this->createTestParser();
4443 $context = 'property SomeClassName::invalidProperty.';
4444 $parser->setTarget(Target::TARGET_PROPERTY);
4445
4446
4447 $docblock = '@Doctrine\Tests\Common\Annotations\Fixtures\AnnotationWithRequiredAttributes("Some Value", annot = @Doctrine\Tests\Common\Annotations\Fixtures\AnnotationTargetAnnotation)';
4448 $result = $parser->parse($docblock);
4449
4450 $this->assertTrue(sizeof($result) === 1);
4451 $this->assertInstanceOf('Doctrine\Tests\Common\Annotations\Fixtures\AnnotationWithRequiredAttributes', $result[0]);
4452 $this->assertEquals("Some Value",$result[0]->getValue());
4453 $this->assertInstanceOf('Doctrine\Tests\Common\Annotations\Fixtures\AnnotationTargetAnnotation', $result[0]->getAnnot());
4454
4455
4456 $docblock = '@Doctrine\Tests\Common\Annotations\Fixtures\AnnotationWithRequiredAttributes("Some Value")';
4457 try {
4458 $result = $parser->parse($docblock,$context);
4459 $this->fail();
4460 } catch (\Doctrine\Common\Annotations\AnnotationException $exc) {
4461 $this->assertContains('Attribute "annot" of @Doctrine\Tests\Common\Annotations\Fixtures\AnnotationWithRequiredAttributes declared on property SomeClassName::invalidProperty. expects a(n) Doctrine\Tests\Common\Annotations\Fixtures\AnnotationTargetAnnotation. This value should not be null.', $exc->getMessage());
4462 }
4463
4464 $docblock = '@Doctrine\Tests\Common\Annotations\Fixtures\AnnotationWithRequiredAttributes(annot = @Doctrine\Tests\Common\Annotations\Fixtures\AnnotationTargetAnnotation)';
4465 try {
4466 $result = $parser->parse($docblock,$context);
4467 $this->fail();
4468 } catch (\Doctrine\Common\Annotations\AnnotationException $exc) {
4469 $this->assertContains('Attribute "value" of @Doctrine\Tests\Common\Annotations\Fixtures\AnnotationWithRequiredAttributes declared on property SomeClassName::invalidProperty. expects a(n) string. This value should not be null.', $exc->getMessage());
4470 }
4471
4472 }
4473
4474 public function testAnnotationWithRequiredAttributesWithoutContructor()
4475 {
4476 $parser = $this->createTestParser();
4477 $context = 'property SomeClassName::invalidProperty.';
4478 $parser->setTarget(Target::TARGET_PROPERTY);
4479
4480
4481 $docblock = '@Doctrine\Tests\Common\Annotations\Fixtures\AnnotationWithRequiredAttributesWithoutContructor("Some Value", annot = @Doctrine\Tests\Common\Annotations\Fixtures\AnnotationTargetAnnotation)';
4482 $result = $parser->parse($docblock);
4483
4484 $this->assertTrue(sizeof($result) === 1);
4485 $this->assertInstanceOf('Doctrine\Tests\Common\Annotations\Fixtures\AnnotationWithRequiredAttributesWithoutContructor', $result[0]);
4486 $this->assertEquals("Some Value", $result[0]->value);
4487 $this->assertInstanceOf('Doctrine\Tests\Common\Annotations\Fixtures\AnnotationTargetAnnotation', $result[0]->annot);
4488
4489
4490 $docblock = '@Doctrine\Tests\Common\Annotations\Fixtures\AnnotationWithRequiredAttributesWithoutContructor("Some Value")';
4491 try {
4492 $result = $parser->parse($docblock,$context);
4493 $this->fail();
4494 } catch (\Doctrine\Common\Annotations\AnnotationException $exc) {
4495 $this->assertContains('Attribute "annot" of @Doctrine\Tests\Common\Annotations\Fixtures\AnnotationWithRequiredAttributesWithoutContructor declared on property SomeClassName::invalidProperty. expects a(n) Doctrine\Tests\Common\Annotations\Fixtures\AnnotationTargetAnnotation. This value should not be null.', $exc->getMessage());
4496 }
4497
4498 $docblock = '@Doctrine\Tests\Common\Annotations\Fixtures\AnnotationWithRequiredAttributesWithoutContructor(annot = @Doctrine\Tests\Common\Annotations\Fixtures\AnnotationTargetAnnotation)';
4499 try {
4500 $result = $parser->parse($docblock,$context);
4501 $this->fail();
4502 } catch (\Doctrine\Common\Annotations\AnnotationException $exc) {
4503 $this->assertContains('Attribute "value" of @Doctrine\Tests\Common\Annotations\Fixtures\AnnotationWithRequiredAttributesWithoutContructor declared on property SomeClassName::invalidProperty. expects a(n) string. This value should not be null.', $exc->getMessage());
4504 }
4505
4506 }
4507
4508 /**
4509 * @expectedException Doctrine\Common\Annotations\AnnotationException
4510 * @expectedExceptionMessage Attribute "value" of @Doctrine\Tests\Common\Annotations\Fixtures\AnnotationEnum declared on property SomeClassName::invalidProperty. accept only [ONE, TWO, THREE], but got FOUR.
4511 */
4512 public function testAnnotationEnumeratorException()
4513 {
4514 $parser = $this->createTestParser();
4515 $context = 'property SomeClassName::invalidProperty.';
4516 $docblock = '@Doctrine\Tests\Common\Annotations\Fixtures\AnnotationEnum("FOUR")';
4517
4518 $parser->setIgnoreNotImportedAnnotations(false);
4519 $parser->setTarget(Target::TARGET_PROPERTY);
4520 $parser->parse($docblock, $context);
4521 }
4522
4523 /**
4524 * @expectedException Doctrine\Common\Annotations\AnnotationException
4525 * @expectedExceptionMessage Attribute "value" of @Doctrine\Tests\Common\Annotations\Fixtures\AnnotationEnumLiteral declared on property SomeClassName::invalidProperty. accept only [AnnotationEnumLiteral::ONE, AnnotationEnumLiteral::TWO, AnnotationEnumLiteral::THREE], but got 4.
4526 */
4527 public function testAnnotationEnumeratorLiteralException()
4528 {
4529 $parser = $this->createTestParser();
4530 $context = 'property SomeClassName::invalidProperty.';
4531 $docblock = '@Doctrine\Tests\Common\Annotations\Fixtures\AnnotationEnumLiteral(4)';
4532
4533 $parser->setIgnoreNotImportedAnnotations(false);
4534 $parser->setTarget(Target::TARGET_PROPERTY);
4535 $parser->parse($docblock, $context);
4536 }
4537
4538 /**
4539 * @expectedException \InvalidArgumentException
4540 * @expectedExceptionMessage @Enum supports only scalar values "array" given.
4541 */
4542 public function testAnnotationEnumInvalidTypeDeclarationException()
4543 {
4544 $parser = $this->createTestParser();
4545 $docblock = '@Doctrine\Tests\Common\Annotations\Fixtures\AnnotationEnumInvalid("foo")';
4546
4547 $parser->setIgnoreNotImportedAnnotations(false);
4548 $parser->parse($docblock);
4549 }
4550
4551 /**
4552 * @expectedException \InvalidArgumentException
4553 * @expectedExceptionMessage Undefined enumerator value "3" for literal "AnnotationEnumLiteral::THREE".
4554 */
4555 public function testAnnotationEnumInvalidLiteralDeclarationException()
4556 {
4557 $parser = $this->createTestParser();
4558 $docblock = '@Doctrine\Tests\Common\Annotations\Fixtures\AnnotationEnumLiteralInvalid("foo")';
4559
4560 $parser->setIgnoreNotImportedAnnotations(false);
4561 $parser->parse($docblock);
4562 }
4563
4564 public function getConstantsProvider()
4565 {
4566 $provider[] = array(
4567 '@AnnotationWithConstants(PHP_EOL)',
4568 PHP_EOL
4569 );
4570 $provider[] = array(
4571 '@AnnotationWithConstants(AnnotationWithConstants::INTEGER)',
4572 AnnotationWithConstants::INTEGER
4573 );
4574 $provider[] = array(
4575 '@Doctrine\Tests\Common\Annotations\Fixtures\AnnotationWithConstants(AnnotationWithConstants::STRING)',
4576 AnnotationWithConstants::STRING
4577 );
4578 $provider[] = array(
4579 '@AnnotationWithConstants(Doctrine\Tests\Common\Annotations\Fixtures\AnnotationWithConstants::FLOAT)',
4580 AnnotationWithConstants::FLOAT
4581 );
4582 $provider[] = array(
4583 '@AnnotationWithConstants(ClassWithConstants::SOME_VALUE)',
4584 ClassWithConstants::SOME_VALUE
4585 );
4586 $provider[] = array(
4587 '@AnnotationWithConstants(Doctrine\Tests\Common\Annotations\Fixtures\ClassWithConstants::SOME_VALUE)',
4588 ClassWithConstants::SOME_VALUE
4589 );
4590 $provider[] = array(
4591 '@AnnotationWithConstants(IntefaceWithConstants::SOME_VALUE)',
4592 IntefaceWithConstants::SOME_VALUE
4593 );
4594 $provider[] = array(
4595 '@AnnotationWithConstants(\Doctrine\Tests\Common\Annotations\Fixtures\IntefaceWithConstants::SOME_VALUE)',
4596 IntefaceWithConstants::SOME_VALUE
4597 );
4598 $provider[] = array(
4599 '@AnnotationWithConstants({AnnotationWithConstants::STRING, AnnotationWithConstants::INTEGER, AnnotationWithConstants::FLOAT})',
4600 array(AnnotationWithConstants::STRING, AnnotationWithConstants::INTEGER, AnnotationWithConstants::FLOAT)
4601 );
4602 $provider[] = array(
4603 '@AnnotationWithConstants({
4604 AnnotationWithConstants::STRING = AnnotationWithConstants::INTEGER
4605 })',
4606 array(AnnotationWithConstants::STRING => AnnotationWithConstants::INTEGER)
4607 );
4608 $provider[] = array(
4609 '@AnnotationWithConstants({
4610 Doctrine\Tests\Common\Annotations\Fixtures\IntefaceWithConstants::SOME_KEY = AnnotationWithConstants::INTEGER
4611 })',
4612 array(IntefaceWithConstants::SOME_KEY => AnnotationWithConstants::INTEGER)
4613 );
4614 $provider[] = array(
4615 '@AnnotationWithConstants({
4616 \Doctrine\Tests\Common\Annotations\Fixtures\IntefaceWithConstants::SOME_KEY = AnnotationWithConstants::INTEGER
4617 })',
4618 array(IntefaceWithConstants::SOME_KEY => AnnotationWithConstants::INTEGER)
4619 );
4620 $provider[] = array(
4621 '@AnnotationWithConstants({
4622 AnnotationWithConstants::STRING = AnnotationWithConstants::INTEGER,
4623 ClassWithConstants::SOME_KEY = ClassWithConstants::SOME_VALUE,
4624 Doctrine\Tests\Common\Annotations\Fixtures\ClassWithConstants::SOME_KEY = IntefaceWithConstants::SOME_VALUE
4625 })',
4626 array(
4627 AnnotationWithConstants::STRING => AnnotationWithConstants::INTEGER,
4628 ClassWithConstants::SOME_KEY => ClassWithConstants::SOME_VALUE,
4629 ClassWithConstants::SOME_KEY => IntefaceWithConstants::SOME_VALUE
4630 )
4631 );
4632 return $provider;
4633 }
4634
4635 /**
4636 * @dataProvider getConstantsProvider
4637 */
4638 public function testSupportClassConstants($docblock, $expected)
4639 {
4640 $parser = $this->createTestParser();
4641 $parser->setImports(array(
4642 'classwithconstants' => 'Doctrine\Tests\Common\Annotations\Fixtures\ClassWithConstants',
4643 'intefacewithconstants' => 'Doctrine\Tests\Common\Annotations\Fixtures\IntefaceWithConstants',
4644 'annotationwithconstants' => 'Doctrine\Tests\Common\Annotations\Fixtures\AnnotationWithConstants'
4645 ));
4646
4647 $result = $parser->parse($docblock);
4648 $this->assertInstanceOf('\Doctrine\Tests\Common\Annotations\Fixtures\AnnotationWithConstants', $annotation = $result[0]);
4649 $this->assertEquals($expected, $annotation->value);
4650 }
4651
4652 /**
4653 * @expectedException Doctrine\Common\Annotations\AnnotationException
4654 * @expectedExceptionMessage The annotation @SomeAnnotationClassNameWithoutConstructorAndProperties declared on does not accept any values, but got {"value":"Foo"}.
4655 */
4656 public function testWithoutConstructorWhenIsNotDefaultValue()
4657 {
4658 $parser = $this->createTestParser();
4659 $docblock = <<<DOCBLOCK
4660 /**
4661 * @SomeAnnotationClassNameWithoutConstructorAndProperties("Foo")
4662 */
4663 DOCBLOCK;
4664
4665
4666 $parser->setTarget(Target::TARGET_CLASS);
4667 $parser->parse($docblock);
4668 }
4669
4670 /**
4671 * @expectedException Doctrine\Common\Annotations\AnnotationException
4672 * @expectedExceptionMessage The annotation @SomeAnnotationClassNameWithoutConstructorAndProperties declared on does not accept any values, but got {"value":"Foo"}.
4673 */
4674 public function testWithoutConstructorWhenHasNoProperties()
4675 {
4676 $parser = $this->createTestParser();
4677 $docblock = <<<DOCBLOCK
4678 /**
4679 * @SomeAnnotationClassNameWithoutConstructorAndProperties(value = "Foo")
4680 */
4681 DOCBLOCK;
4682
4683 $parser->setTarget(Target::TARGET_CLASS);
4684 $parser->parse($docblock);
4685 }
4686
4687 /**
4688 * @expectedException Doctrine\Common\Annotations\AnnotationException
4689 * @expectedExceptionMessage Expected namespace separator or identifier, got ')' at position 24 in class @Doctrine\Tests\Common\Annotations\Fixtures\AnnotationWithTargetSyntaxError.
4690 */
4691 public function testAnnotationTargetSyntaxError()
4692 {
4693 $parser = $this->createTestParser();
4694 $context = 'class ' . 'SomeClassName';
4695 $docblock = <<<DOCBLOCK
4696 /**
4697 * @Doctrine\Tests\Common\Annotations\Fixtures\AnnotationWithTargetSyntaxError()
4698 */
4699 DOCBLOCK;
4700
4701 $parser->setTarget(Target::TARGET_CLASS);
4702 $parser->parse($docblock,$context);
4703 }
4704
4705 /**
4706 * @expectedException \InvalidArgumentException
4707 * @expectedExceptionMessage Invalid Target "Foo". Available targets: [ALL, CLASS, METHOD, PROPERTY, ANNOTATION]
4708 */
4709 public function testAnnotationWithInvalidTargetDeclarationError()
4710 {
4711 $parser = $this->createTestParser();
4712 $context = 'class ' . 'SomeClassName';
4713 $docblock = <<<DOCBLOCK
4714 /**
4715 * @AnnotationWithInvalidTargetDeclaration()
4716 */
4717 DOCBLOCK;
4718
4719 $parser->setTarget(Target::TARGET_CLASS);
4720 $parser->parse($docblock,$context);
4721 }
4722
4723 /**
4724 * @expectedException \InvalidArgumentException
4725 * @expectedExceptionMessage @Target expects either a string value, or an array of strings, "NULL" given.
4726 */
4727 public function testAnnotationWithTargetEmptyError()
4728 {
4729 $parser = $this->createTestParser();
4730 $context = 'class ' . 'SomeClassName';
4731 $docblock = <<<DOCBLOCK
4732 /**
4733 * @AnnotationWithTargetEmpty()
4734 */
4735 DOCBLOCK;
4736
4737 $parser->setTarget(Target::TARGET_CLASS);
4738 $parser->parse($docblock,$context);
4739 }
4740
4741 /**
4742 * @group DDC-575
4743 */
4744 public function testRegressionDDC575()
4745 {
4746 $parser = $this->createTestParser();
4747
4748 $docblock = <<<DOCBLOCK
4749 /**
4750 * @Name
4751 *
4752 * Will trigger error.
4753 */
4754 DOCBLOCK;
4755
4756 $result = $parser->parse($docblock);
4757
4758 $this->assertInstanceOf("Doctrine\Tests\Common\Annotations\Name", $result[0]);
4759
4760 $docblock = <<<DOCBLOCK
4761 /**
4762 * @Name
4763 * @Marker
4764 *
4765 * Will trigger error.
4766 */
4767 DOCBLOCK;
4768
4769 $result = $parser->parse($docblock);
4770
4771 $this->assertInstanceOf("Doctrine\Tests\Common\Annotations\Name", $result[0]);
4772 }
4773
4774 /**
4775 * @group DDC-77
4776 */
4777 public function testAnnotationWithoutClassIsIgnoredWithoutWarning()
4778 {
4779 $parser = new DocParser();
4780 $parser->setIgnoreNotImportedAnnotations(true);
4781 $result = $parser->parse("@param");
4782
4783 $this->assertEquals(0, count($result));
4784 }
4785
4786 /**
4787 * @expectedException Doctrine\Common\Annotations\AnnotationException
4788 * @expectedExceptionMessage Expected PlainValue, got ''' at position 10.
4789 */
4790 public function testAnnotationDontAcceptSingleQuotes()
4791 {
4792 $parser = $this->createTestParser();
4793 $parser->parse("@Name(foo='bar')");
4794 }
4795
4796 /**
4797 * @group DCOM-41
4798 */
4799 public function testAnnotationDoesntThrowExceptionWhenAtSignIsNotFollowedByIdentifier()
4800 {
4801 $parser = new DocParser();
4802 $result = $parser->parse("'@'");
4803
4804 $this->assertEquals(0, count($result));
4805 }
4806
4807 /**
4808 * @group DCOM-41
4809 * @expectedException Doctrine\Common\Annotations\AnnotationException
4810 */
4811 public function testAnnotationThrowsExceptionWhenAtSignIsNotFollowedByIdentifierInNestedAnnotation()
4812 {
4813 $parser = new DocParser();
4814 $result = $parser->parse("@Doctrine\Tests\Common\Annotations\Name(@')");
4815 }
4816
4817 /**
4818 * @group DCOM-56
4819 */
4820 public function testAutoloadAnnotation()
4821 {
4822 $this->assertFalse(class_exists('Doctrine\Tests\Common\Annotations\Fixture\Annotation\Autoload', false), 'Pre-condition: Doctrine\Tests\Common\Annotations\Fixture\Annotation\Autoload not allowed to be loaded.');
4823
4824 $parser = new DocParser();
4825
4826 AnnotationRegistry::registerAutoloadNamespace('Doctrine\Tests\Common\Annotations\Fixtures\Annotation', __DIR__ . '/../../../../');
4827
4828 $parser->setImports(array(
4829 'autoload' => 'Doctrine\Tests\Common\Annotations\Fixtures\Annotation\Autoload',
4830 ));
4831 $annotations = $parser->parse('@Autoload');
4832
4833 $this->assertEquals(1, count($annotations));
4834 $this->assertInstanceOf('Doctrine\Tests\Common\Annotations\Fixtures\Annotation\Autoload', $annotations[0]);
4835 }
4836
4837 public function createTestParser()
4838 {
4839 $parser = new DocParser();
4840 $parser->setIgnoreNotImportedAnnotations(true);
4841 $parser->setImports(array(
4842 'name' => 'Doctrine\Tests\Common\Annotations\Name',
4843 '__NAMESPACE__' => 'Doctrine\Tests\Common\Annotations',
4844 ));
4845
4846 return $parser;
4847 }
4848
4849 /**
4850 * @group DDC-78
4851 * @expectedException Doctrine\Common\Annotations\AnnotationException
4852 * @expectedExceptionMessage Expected PlainValue, got ''' at position 10 in class \Doctrine\Tests\Common\Annotations\Name
4853 */
4854 public function testSyntaxErrorWithContextDescription()
4855 {
4856 $parser = $this->createTestParser();
4857 $parser->parse("@Name(foo='bar')", "class \Doctrine\Tests\Common\Annotations\Name");
4858 }
4859
4860 /**
4861 * @group DDC-183
4862 */
4863 public function testSyntaxErrorWithUnknownCharacters()
4864 {
4865 $docblock = <<<DOCBLOCK
4866 /**
4867 * @test at.
4868 */
4869 class A {
4870 }
4871 DOCBLOCK;
4872
4873 //$lexer = new \Doctrine\Common\Annotations\Lexer();
4874 //$lexer->setInput(trim($docblock, '/ *'));
4875 //var_dump($lexer);
4876
4877 try {
4878 $parser = $this->createTestParser();
4879 $result = $parser->parse($docblock);
4880 } catch (Exception $e) {
4881 $this->fail($e->getMessage());
4882 }
4883 }
4884
4885 /**
4886 * @group DCOM-14
4887 */
4888 public function testIgnorePHPDocThrowTag()
4889 {
4890 $docblock = <<<DOCBLOCK
4891 /**
4892 * @throws \RuntimeException
4893 */
4894 class A {
4895 }
4896 DOCBLOCK;
4897
4898 try {
4899 $parser = $this->createTestParser();
4900 $result = $parser->parse($docblock);
4901 } catch (Exception $e) {
4902 $this->fail($e->getMessage());
4903 }
4904 }
4905
4906 /**
4907 * @group DCOM-38
4908 */
4909 public function testCastInt()
4910 {
4911 $parser = $this->createTestParser();
4912
4913 $result = $parser->parse("@Name(foo=1234)");
4914 $annot = $result[0];
4915 $this->assertInternalType('int', $annot->foo);
4916 }
4917
4918 /**
4919 * @group DCOM-38
4920 */
4921 public function testCastNegativeInt()
4922 {
4923 $parser = $this->createTestParser();
4924
4925 $result = $parser->parse("@Name(foo=-1234)");
4926 $annot = $result[0];
4927 $this->assertInternalType('int', $annot->foo);
4928 }
4929
4930 /**
4931 * @group DCOM-38
4932 */
4933 public function testCastFloat()
4934 {
4935 $parser = $this->createTestParser();
4936
4937 $result = $parser->parse("@Name(foo=1234.345)");
4938 $annot = $result[0];
4939 $this->assertInternalType('float', $annot->foo);
4940 }
4941
4942 /**
4943 * @group DCOM-38
4944 */
4945 public function testCastNegativeFloat()
4946 {
4947 $parser = $this->createTestParser();
4948
4949 $result = $parser->parse("@Name(foo=-1234.345)");
4950 $annot = $result[0];
4951 $this->assertInternalType('float', $annot->foo);
4952
4953 $result = $parser->parse("@Marker(-1234.345)");
4954 $annot = $result[0];
4955 $this->assertInternalType('float', $annot->value);
4956 }
4957
4958 public function testReservedKeywordsInAnnotations()
4959 {
4960 $parser = $this->createTestParser();
4961
4962 $result = $parser->parse('@Doctrine\Tests\Common\Annotations\True');
4963 $this->assertTrue($result[0] instanceof True);
4964 $result = $parser->parse('@Doctrine\Tests\Common\Annotations\False');
4965 $this->assertTrue($result[0] instanceof False);
4966 $result = $parser->parse('@Doctrine\Tests\Common\Annotations\Null');
4967 $this->assertTrue($result[0] instanceof Null);
4968
4969 $result = $parser->parse('@True');
4970 $this->assertTrue($result[0] instanceof True);
4971 $result = $parser->parse('@False');
4972 $this->assertTrue($result[0] instanceof False);
4973 $result = $parser->parse('@Null');
4974 $this->assertTrue($result[0] instanceof Null);
4975 }
4976
4977 /**
4978 * @expectedException Doctrine\Common\Annotations\AnnotationException
4979 * @expectedExceptionMessage [Creation Error] The annotation @SomeAnnotationClassNameWithoutConstructor declared on some class does not have a property named "invalidaProperty". Available properties: data, name
4980 */
4981 public function testSetValuesExeption()
4982 {
4983 $docblock = <<<DOCBLOCK
4984 /**
4985 * @SomeAnnotationClassNameWithoutConstructor(invalidaProperty = "Some val")
4986 */
4987 DOCBLOCK;
4988
4989 $this->createTestParser()->parse($docblock, 'some class');
4990 }
4991
4992 /**
4993 * @expectedException Doctrine\Common\Annotations\AnnotationException
4994 * @expectedExceptionMessage [Syntax Error] Expected Doctrine\Common\Annotations\DocLexer::T_IDENTIFIER or Doctrine\Common\Annotations\DocLexer::T_TRUE or Doctrine\Common\Annotations\DocLexer::T_FALSE or Doctrine\Common\Annotations\DocLexer::T_NULL, got '3.42' at position 5.
4995 */
4996 public function testInvalidIdentifierInAnnotation()
4997 {
4998 $parser = $this->createTestParser();
4999 $parser->parse('@Foo\3.42');
5000 }
5001
5002 public function testTrailingCommaIsAllowed()
5003 {
5004 $parser = $this->createTestParser();
5005
5006 $annots = $parser->parse('@Name({
5007 "Foo",
5008 "Bar",
5009 })');
5010 $this->assertEquals(1, count($annots));
5011 $this->assertEquals(array('Foo', 'Bar'), $annots[0]->value);
5012 }
5013
5014 public function testDefaultAnnotationValueIsNotOverwritten()
5015 {
5016 $parser = $this->createTestParser();
5017
5018 $annots = $parser->parse('@Doctrine\Tests\Common\Annotations\Fixtures\Annotation\AnnotWithDefaultValue');
5019 $this->assertEquals(1, count($annots));
5020 $this->assertEquals('bar', $annots[0]->foo);
5021 }
5022
5023 public function testArrayWithColon()
5024 {
5025 $parser = $this->createTestParser();
5026
5027 $annots = $parser->parse('@Name({"foo": "bar"})');
5028 $this->assertEquals(1, count($annots));
5029 $this->assertEquals(array('foo' => 'bar'), $annots[0]->value);
5030 }
5031
5032 /**
5033 * @expectedException Doctrine\Common\Annotations\AnnotationException
5034 * @expectedExceptionMessage [Semantical Error] Couldn't find constant foo.
5035 */
5036 public function testInvalidContantName()
5037 {
5038 $parser = $this->createTestParser();
5039 $parser->parse('@Name(foo: "bar")');
5040 }
5041
5042 /**
5043 * Tests parsing empty arrays.
5044 */
5045 public function testEmptyArray()
5046 {
5047 $parser = $this->createTestParser();
5048
5049 $annots = $parser->parse('@Name({"foo": {}})');
5050 $this->assertEquals(1, count($annots));
5051 $this->assertEquals(array('foo' => array()), $annots[0]->value);
5052 }
5053 }
5054
5055 /** @Annotation */
5056 class SomeAnnotationClassNameWithoutConstructor
5057 {
5058 public $data;
5059 public $name;
5060 }
5061
5062 /** @Annotation */
5063 class SomeAnnotationWithConstructorWithoutParams
5064 {
5065 function __construct()
5066 {
5067 $this->data = "Some data";
5068 }
5069 public $data;
5070 public $name;
5071 }
5072
5073 /** @Annotation */
5074 class SomeAnnotationClassNameWithoutConstructorAndProperties{}
5075
5076 /**
5077 * @Annotation
5078 * @Target("Foo")
5079 */
5080 class AnnotationWithInvalidTargetDeclaration{}
5081
5082 /**
5083 * @Annotation
5084 * @Target
5085 */
5086 class AnnotationWithTargetEmpty{}
5087
5088 /** @Annotation */
5089 class AnnotationExtendsAnnotationTargetAll extends \Doctrine\Tests\Common\Annotations\Fixtures\AnnotationTargetAll
5090 {
5091 }
5092
5093 /** @Annotation */
5094 class Name extends \Doctrine\Common\Annotations\Annotation {
5095 public $foo;
5096 }
5097
5098 /** @Annotation */
5099 class Marker {
5100 public $value;
5101 }
5102
5103 /** @Annotation */
5104 class True {}
5105
5106 /** @Annotation */
5107 class False {}
5108
5109 /** @Annotation */
5110 class Null {}
5111
5112 namespace Doctrine\Tests\Common\Annotations\FooBar;
5113
5114 /** @Annotation */
5115 class Name extends \Doctrine\Common\Annotations\Annotation {
5116 }
5117 <?php
5118 namespace Doctrine\Tests\Common\Annotations\Ticket;
5119
5120 //Some class named Entity in the global namespace
5121 include __DIR__ .'/DCOM58Entity.php';
5122
5123 /**
5124 * @group DCOM58
5125 */
5126 class DCOM58Test extends \PHPUnit_Framework_TestCase
5127 {
5128 public function testIssue()
5129 {
5130 $reader = new \Doctrine\Common\Annotations\AnnotationReader();
5131 $result = $reader->getClassAnnotations(new \ReflectionClass(__NAMESPACE__."\MappedClass"));
5132
5133 foreach ($result as $annot) {
5134 $classAnnotations[get_class($annot)] = $annot;
5135 }
5136
5137 $this->assertTrue(!isset($classAnnotations['']), 'Class "xxx" is not a valid entity or mapped super class.');
5138 }
5139
5140 public function testIssueGlobalNamespace()
5141 {
5142 $docblock = "@Entity";
5143 $parser = new \Doctrine\Common\Annotations\DocParser();
5144 $parser->setImports(array(
5145 "__NAMESPACE__" =>"Doctrine\Tests\Common\Annotations\Ticket\Doctrine\ORM\Mapping"
5146 ));
5147
5148 $annots = $parser->parse($docblock);
5149
5150 $this->assertEquals(1, count($annots));
5151 $this->assertInstanceOf("Doctrine\Tests\Common\Annotations\Ticket\Doctrine\ORM\Mapping\Entity", $annots[0]);
5152 }
5153
5154 public function testIssueNamespaces()
5155 {
5156 $docblock = "@Entity";
5157 $parser = new \Doctrine\Common\Annotations\DocParser();
5158 $parser->addNamespace("Doctrine\Tests\Common\Annotations\Ticket\Doctrine\ORM");
5159
5160 $annots = $parser->parse($docblock);
5161
5162 $this->assertEquals(1, count($annots));
5163 $this->assertInstanceOf("Doctrine\Tests\Common\Annotations\Ticket\Doctrine\ORM\Entity", $annots[0]);
5164 }
5165
5166 public function testIssueMultipleNamespaces()
5167 {
5168 $docblock = "@Entity";
5169 $parser = new \Doctrine\Common\Annotations\DocParser();
5170 $parser->addNamespace("Doctrine\Tests\Common\Annotations\Ticket\Doctrine\ORM\Mapping");
5171 $parser->addNamespace("Doctrine\Tests\Common\Annotations\Ticket\Doctrine\ORM");
5172
5173 $annots = $parser->parse($docblock);
5174
5175 $this->assertEquals(1, count($annots));
5176 $this->assertInstanceOf("Doctrine\Tests\Common\Annotations\Ticket\Doctrine\ORM\Mapping\Entity", $annots[0]);
5177 }
5178
5179 public function testIssueWithNamespacesOrImports()
5180 {
5181 $docblock = "@Entity";
5182 $parser = new \Doctrine\Common\Annotations\DocParser();
5183 $annots = $parser->parse($docblock);
5184
5185 $this->assertEquals(1, count($annots));
5186 $this->assertInstanceOf("Entity", $annots[0]);
5187 $this->assertEquals(1, count($annots));
5188 }
5189
5190
5191 public function testIssueSimpleAnnotationReader()
5192 {
5193 $reader = new \Doctrine\Common\Annotations\SimpleAnnotationReader();
5194 $reader->addNamespace('Doctrine\Tests\Common\Annotations\Ticket\Doctrine\ORM\Mapping');
5195 $annots = $reader->getClassAnnotations(new \ReflectionClass(__NAMESPACE__."\MappedClass"));
5196
5197 $this->assertEquals(1, count($annots));
5198 $this->assertInstanceOf("Doctrine\Tests\Common\Annotations\Ticket\Doctrine\ORM\Mapping\Entity", $annots[0]);
5199 }
5200
5201 }
5202
5203 /**
5204 * @Entity
5205 */
5206 class MappedClass
5207 {
5208
5209 }
5210
5211
5212 namespace Doctrine\Tests\Common\Annotations\Ticket\Doctrine\ORM\Mapping;
5213 /**
5214 * @Annotation
5215 */
5216 class Entity
5217 {
5218
5219 }
5220
5221 namespace Doctrine\Tests\Common\Annotations\Ticket\Doctrine\ORM;
5222 /**
5223 * @Annotation
5224 */
5225 class Entity
5226 {
5227
5228 }
5229 <?php
5230
5231 namespace Doctrine\Tests\Common\Annotations\Ticket;
5232
5233 use Doctrine\Tests\Common\Annotations\Fixtures\Controller;
5234
5235 /**
5236 * @group
5237 */
5238 class DCOM55Test extends \PHPUnit_Framework_TestCase
5239 {
5240 /**
5241 * @expectedException Doctrine\Common\Annotations\AnnotationException
5242 * @expectedExceptionMessage [Semantical Error] The class "Doctrine\Tests\Common\Annotations\Fixtures\Controller" is not annotated with @Annotation. Are you sure this class can be used as annotation? If so, then you need to add @Annotation to the _class_ doc comment of "Doctrine\Tests\Common\Annotations\Fixtures\Controller". If it is indeed no annotation, then you need to add @IgnoreAnnotation("Controller") to the _class_ doc comment of class Doctrine\Tests\Common\Annotations\Ticket\Dummy.
5243 */
5244 public function testIssue()
5245 {
5246 $class = new \ReflectionClass(__NAMESPACE__ . '\\Dummy');
5247 $reader = new \Doctrine\Common\Annotations\AnnotationReader();
5248 $reader->getClassAnnotations($class);
5249 }
5250
5251 public function testAnnotation()
5252 {
5253 $class = new \ReflectionClass(__NAMESPACE__ . '\\DCOM55Consumer');
5254 $reader = new \Doctrine\Common\Annotations\AnnotationReader();
5255 $annots = $reader->getClassAnnotations($class);
5256
5257 $this->assertEquals(1, count($annots));
5258 $this->assertInstanceOf(__NAMESPACE__.'\\DCOM55Annotation', $annots[0]);
5259 }
5260
5261 public function testParseAnnotationDocblocks()
5262 {
5263 $class = new \ReflectionClass(__NAMESPACE__ . '\\DCOM55Annotation');
5264 $reader = new \Doctrine\Common\Annotations\AnnotationReader();
5265 $annots = $reader->getClassAnnotations($class);
5266
5267 $this->assertEquals(0, count($annots));
5268 }
5269 }
5270
5271 /**
5272 * @Controller
5273 */
5274 class Dummy
5275 {
5276
5277 }
5278
5279 /**
5280 * @Annotation
5281 */
5282 class DCOM55Annotation
5283 {
5284
5285 }
5286
5287 /**
5288 * @DCOM55Annotation
5289 */
5290 class DCOM55Consumer
5291 {
5292
5293 }<?php
5294 // Some class named Entity in the global namespace
5295 /**
5296 * @Annotation
5297 */
5298 class Entity
5299 {
5300 }
5301 <?php
5302
5303 namespace Doctrine\Tests\Common\Annotations;
5304
5305 use Doctrine\Common\Annotations\DoctrineReader;
5306 use Doctrine\Common\Annotations\Annotation\IgnoreAnnotation;
5307 use Doctrine\Common\Annotations\Annotation\IgnorePhpDoc;
5308 use ReflectionClass, Doctrine\Common\Annotations\AnnotationReader;
5309
5310 use Doctrine\Tests\Common\Annotations\DummyAnnotation;
5311 use Doctrine\Tests\Common\Annotations\Name;
5312 use Doctrine\Tests\Common\Annotations\DummyId;
5313 use Doctrine\Tests\Common\Annotations\DummyJoinTable;
5314 use Doctrine\Tests\Common\Annotations\DummyJoinColumn;
5315 use Doctrine\Tests\Common\Annotations\DummyColumn;
5316 use Doctrine\Tests\Common\Annotations\DummyGeneratedValue;
5317
5318 require_once __DIR__ . '/TopLevelAnnotation.php';
5319
5320 abstract class AbstractReaderTest extends \PHPUnit_Framework_TestCase
5321 {
5322 public function getReflectionClass()
5323 {
5324 $className = 'Doctrine\Tests\Common\Annotations\DummyClass';
5325 return new ReflectionClass($className);
5326 }
5327
5328 public function testAnnotations()
5329 {
5330 $class = $this->getReflectionClass();
5331 $reader = $this->getReader();
5332
5333 $this->assertEquals(1, count($reader->getClassAnnotations($class)));
5334 $this->assertInstanceOf($annotName = 'Doctrine\Tests\Common\Annotations\DummyAnnotation', $annot = $reader->getClassAnnotation($class, $annotName));
5335 $this->assertEquals("hello", $annot->dummyValue);
5336
5337 $field1Prop = $class->getProperty('field1');
5338 $propAnnots = $reader->getPropertyAnnotations($field1Prop);
5339 $this->assertEquals(1, count($propAnnots));
5340 $this->assertInstanceOf($annotName, $annot = $reader->getPropertyAnnotation($field1Prop, $annotName));
5341 $this->assertEquals("fieldHello", $annot->dummyValue);
5342
5343 $getField1Method = $class->getMethod('getField1');
5344 $methodAnnots = $reader->getMethodAnnotations($getField1Method);
5345 $this->assertEquals(1, count($methodAnnots));
5346 $this->assertInstanceOf($annotName, $annot = $reader->getMethodAnnotation($getField1Method, $annotName));
5347 $this->assertEquals(array(1, 2, "three"), $annot->value);
5348
5349 $field2Prop = $class->getProperty('field2');
5350 $propAnnots = $reader->getPropertyAnnotations($field2Prop);
5351 $this->assertEquals(1, count($propAnnots));
5352 $this->assertInstanceOf($annotName = 'Doctrine\Tests\Common\Annotations\DummyJoinTable', $joinTableAnnot = $reader->getPropertyAnnotation($field2Prop, $annotName));
5353 $this->assertEquals(1, count($joinTableAnnot->joinColumns));
5354 $this->assertEquals(1, count($joinTableAnnot->inverseJoinColumns));
5355 $this->assertTrue($joinTableAnnot->joinColumns[0] instanceof DummyJoinColumn);
5356 $this->assertTrue($joinTableAnnot->inverseJoinColumns[0] instanceof DummyJoinColumn);
5357 $this->assertEquals('col1', $joinTableAnnot->joinColumns[0]->name);
5358 $this->assertEquals('col2', $joinTableAnnot->joinColumns[0]->referencedColumnName);
5359 $this->assertEquals('col3', $joinTableAnnot->inverseJoinColumns[0]->name);
5360 $this->assertEquals('col4', $joinTableAnnot->inverseJoinColumns[0]->referencedColumnName);
5361
5362 $dummyAnnot = $reader->getMethodAnnotation($class->getMethod('getField1'), 'Doctrine\Tests\Common\Annotations\DummyAnnotation');
5363 $this->assertEquals('', $dummyAnnot->dummyValue);
5364 $this->assertEquals(array(1, 2, 'three'), $dummyAnnot->value);
5365
5366 $dummyAnnot = $reader->getPropertyAnnotation($class->getProperty('field1'), 'Doctrine\Tests\Common\Annotations\DummyAnnotation');
5367 $this->assertEquals('fieldHello', $dummyAnnot->dummyValue);
5368
5369 $classAnnot = $reader->getClassAnnotation($class, 'Doctrine\Tests\Common\Annotations\DummyAnnotation');
5370 $this->assertEquals('hello', $classAnnot->dummyValue);
5371 }
5372
5373 public function testAnnotationsWithValidTargets()
5374 {
5375 $reader = $this->getReader();
5376 $class = new ReflectionClass('Doctrine\Tests\Common\Annotations\Fixtures\ClassWithValidAnnotationTarget');
5377
5378 $this->assertEquals(1,count($reader->getClassAnnotations($class)));
5379 $this->assertEquals(1,count($reader->getPropertyAnnotations($class->getProperty('foo'))));
5380 $this->assertEquals(1,count($reader->getMethodAnnotations($class->getMethod('someFunction'))));
5381 $this->assertEquals(1,count($reader->getPropertyAnnotations($class->getProperty('nested'))));
5382 }
5383
5384 public function testAnnotationsWithVarType()
5385 {
5386 $reader = $this->getReader();
5387 $class = new ReflectionClass('Doctrine\Tests\Common\Annotations\Fixtures\ClassWithAnnotationWithVarType');
5388
5389 $this->assertEquals(1,count($fooAnnot = $reader->getPropertyAnnotations($class->getProperty('foo'))));
5390 $this->assertEquals(1,count($barAnnot = $reader->getMethodAnnotations($class->getMethod('bar'))));
5391
5392 $this->assertInternalType('string', $fooAnnot[0]->string);
5393 $this->assertInstanceOf('Doctrine\Tests\Common\Annotations\Fixtures\AnnotationTargetAll', $barAnnot[0]->annotation);
5394 }
5395
5396 /**
5397 * @expectedException Doctrine\Common\Annotations\AnnotationException
5398 * @expectedExceptionMessage [Semantical Error] Annotation @AnnotationTargetPropertyMethod is not allowed to be declared on class Doctrine\Tests\Common\Annotations\Fixtures\ClassWithInvalidAnnotationTargetAtClass. You may only use this annotation on these code elements: METHOD, PROPERTY
5399 */
5400 public function testClassWithInvalidAnnotationTargetAtClassDocBlock()
5401 {
5402 $reader = $this->getReader();
5403 $reader->getClassAnnotations(new \ReflectionClass('Doctrine\Tests\Common\Annotations\Fixtures\ClassWithInvalidAnnotationTargetAtClass'));
5404 }
5405
5406 public function testClassWithWithInclude()
5407 {
5408 $reader = $this->getReader();
5409 $annots = $reader->getClassAnnotations(new \ReflectionClass('Doctrine\Tests\Common\Annotations\Fixtures\ClassWithRequire'));
5410 $this->assertCount(1, $annots);
5411 }
5412
5413 /**
5414 * @expectedException Doctrine\Common\Annotations\AnnotationException
5415 * @expectedExceptionMessage [Semantical Error] Annotation @AnnotationTargetClass is not allowed to be declared on property Doctrine\Tests\Common\Annotations\Fixtures\ClassWithInvalidAnnotationTargetAtProperty::$foo. You may only use this annotation on these code elements: CLASS
5416 */
5417 public function testClassWithInvalidAnnotationTargetAtPropertyDocBlock()
5418 {
5419 $reader = $this->getReader();
5420 $reader->getPropertyAnnotations(new \ReflectionProperty('Doctrine\Tests\Common\Annotations\Fixtures\ClassWithInvalidAnnotationTargetAtProperty', 'foo'));
5421 }
5422
5423 /**
5424 * @expectedException Doctrine\Common\Annotations\AnnotationException
5425 * @expectedExceptionMessage [Semantical Error] Annotation @AnnotationTargetAnnotation is not allowed to be declared on property Doctrine\Tests\Common\Annotations\Fixtures\ClassWithInvalidAnnotationTargetAtProperty::$bar. You may only use this annotation on these code elements: ANNOTATION
5426 */
5427 public function testClassWithInvalidNestedAnnotationTargetAtPropertyDocBlock()
5428 {
5429 $reader = $this->getReader();
5430 $reader->getPropertyAnnotations(new \ReflectionProperty('Doctrine\Tests\Common\Annotations\Fixtures\ClassWithInvalidAnnotationTargetAtProperty', 'bar'));
5431 }
5432
5433 /**
5434 * @expectedException Doctrine\Common\Annotations\AnnotationException
5435 * @expectedExceptionMessage [Semantical Error] Annotation @AnnotationTargetClass is not allowed to be declared on method Doctrine\Tests\Common\Annotations\Fixtures\ClassWithInvalidAnnotationTargetAtMethod::functionName(). You may only use this annotation on these code elements: CLASS
5436 */
5437 public function testClassWithInvalidAnnotationTargetAtMethodDocBlock()
5438 {
5439 $reader = $this->getReader();
5440 $reader->getMethodAnnotations(new \ReflectionMethod('Doctrine\Tests\Common\Annotations\Fixtures\ClassWithInvalidAnnotationTargetAtMethod', 'functionName'));
5441 }
5442
5443 /**
5444 * @expectedException Doctrine\Common\Annotations\AnnotationException
5445 * @expectedExceptionMessage Expected namespace separator or identifier, got ')' at position 24 in class @Doctrine\Tests\Common\Annotations\Fixtures\AnnotationWithTargetSyntaxError.
5446 */
5447 public function testClassWithAnnotationWithTargetSyntaxErrorAtClassDocBlock()
5448 {
5449 $reader = $this->getReader();
5450 $reader->getClassAnnotations(new \ReflectionClass('Doctrine\Tests\Common\Annotations\Fixtures\ClassWithAnnotationWithTargetSyntaxError'));
5451 }
5452
5453 /**
5454 * @expectedException Doctrine\Common\Annotations\AnnotationException
5455 * @expectedExceptionMessage Expected namespace separator or identifier, got ')' at position 24 in class @Doctrine\Tests\Common\Annotations\Fixtures\AnnotationWithTargetSyntaxError.
5456 */
5457 public function testClassWithAnnotationWithTargetSyntaxErrorAtPropertyDocBlock()
5458 {
5459 $reader = $this->getReader();
5460 $reader->getPropertyAnnotations(new \ReflectionProperty('Doctrine\Tests\Common\Annotations\Fixtures\ClassWithAnnotationWithTargetSyntaxError','foo'));
5461 }
5462
5463 /**
5464 * @expectedException Doctrine\Common\Annotations\AnnotationException
5465 * @expectedExceptionMessage Expected namespace separator or identifier, got ')' at position 24 in class @Doctrine\Tests\Common\Annotations\Fixtures\AnnotationWithTargetSyntaxError.
5466 */
5467 public function testClassWithAnnotationWithTargetSyntaxErrorAtMethodDocBlock()
5468 {
5469 $reader = $this->getReader();
5470 $reader->getMethodAnnotations(new \ReflectionMethod('Doctrine\Tests\Common\Annotations\Fixtures\ClassWithAnnotationWithTargetSyntaxError','bar'));
5471 }
5472
5473 /**
5474 * @expectedException Doctrine\Common\Annotations\AnnotationException
5475 * @expectedExceptionMessage [Type Error] Attribute "string" of @AnnotationWithVarType declared on property Doctrine\Tests\Common\Annotations\Fixtures\ClassWithAnnotationWithVarType::$invalidProperty expects a(n) string, but got integer.
5476 */
5477 public function testClassWithPropertyInvalidVarTypeError()
5478 {
5479 $reader = $this->getReader();
5480 $class = new ReflectionClass('Doctrine\Tests\Common\Annotations\Fixtures\ClassWithAnnotationWithVarType');
5481
5482 $reader->getPropertyAnnotations($class->getProperty('invalidProperty'));
5483 }
5484
5485 /**
5486 * @expectedException Doctrine\Common\Annotations\AnnotationException
5487 * @expectedExceptionMessage [Type Error] Attribute "annotation" of @AnnotationWithVarType declared on method Doctrine\Tests\Common\Annotations\Fixtures\ClassWithAnnotationWithVarType::invalidMethod() expects a(n) Doctrine\Tests\Common\Annotations\Fixtures\AnnotationTargetAll, but got an instance of Doctrine\Tests\Common\Annotations\Fixtures\AnnotationTargetAnnotation.
5488 */
5489 public function testClassWithMethodInvalidVarTypeError()
5490 {
5491 $reader = $this->getReader();
5492 $class = new ReflectionClass('Doctrine\Tests\Common\Annotations\Fixtures\ClassWithAnnotationWithVarType');
5493
5494 $reader->getMethodAnnotations($class->getMethod('invalidMethod'));
5495 }
5496
5497 /**
5498 * @expectedException Doctrine\Common\Annotations\AnnotationException
5499 * @expectedExceptionMessage Expected namespace separator or identifier, got ')' at position 18 in class Doctrine\Tests\Common\Annotations\DummyClassSyntaxError.
5500 */
5501 public function testClassSyntaxErrorContext()
5502 {
5503 $reader = $this->getReader();
5504 $reader->getClassAnnotations(new \ReflectionClass('Doctrine\Tests\Common\Annotations\DummyClassSyntaxError'));
5505 }
5506
5507 /**
5508 * @expectedException Doctrine\Common\Annotations\AnnotationException
5509 * @expectedExceptionMessage Expected namespace separator or identifier, got ')' at position 18 in method Doctrine\Tests\Common\Annotations\DummyClassMethodSyntaxError::foo().
5510 */
5511 public function testMethodSyntaxErrorContext()
5512 {
5513 $reader = $this->getReader();
5514 $reader->getMethodAnnotations(new \ReflectionMethod('Doctrine\Tests\Common\Annotations\DummyClassMethodSyntaxError', 'foo'));
5515 }
5516
5517 /**
5518 * @expectedException Doctrine\Common\Annotations\AnnotationException
5519 * @expectedExceptionMessage Expected namespace separator or identifier, got ')' at position 18 in property Doctrine\Tests\Common\Annotations\DummyClassPropertySyntaxError::$foo.
5520 */
5521 public function testPropertySyntaxErrorContext()
5522 {
5523 $reader = $this->getReader();
5524 $reader->getPropertyAnnotations(new \ReflectionProperty('Doctrine\Tests\Common\Annotations\DummyClassPropertySyntaxError', 'foo'));
5525 }
5526
5527 /**
5528 * @group regression
5529 */
5530 public function testMultipleAnnotationsOnSameLine()
5531 {
5532 $reader = $this->getReader();
5533 $annots = $reader->getPropertyAnnotations(new \ReflectionProperty('Doctrine\Tests\Common\Annotations\DummyClass2', 'id'));
5534 $this->assertEquals(3, count($annots));
5535 }
5536
5537 public function testNonAnnotationProblem()
5538 {
5539 $reader = $this->getReader();
5540
5541 $this->assertNotNull($annot = $reader->getPropertyAnnotation(new \ReflectionProperty('Doctrine\Tests\Common\Annotations\DummyClassNonAnnotationProblem', 'foo'), $name = 'Doctrine\Tests\Common\Annotations\DummyAnnotation'));
5542 $this->assertInstanceOf($name, $annot);
5543 }
5544
5545 public function testImportWithConcreteAnnotation()
5546 {
5547 $reader = $this->getReader();
5548 $property = new \ReflectionProperty('Doctrine\Tests\Common\Annotations\TestImportWithConcreteAnnotation', 'field');
5549 $annotations = $reader->getPropertyAnnotations($property);
5550 $this->assertEquals(1, count($annotations));
5551 $this->assertNotNull($reader->getPropertyAnnotation($property, 'Doctrine\Tests\Common\Annotations\DummyAnnotation'));
5552 }
5553
5554 public function testImportWithInheritance()
5555 {
5556 $reader = $this->getReader();
5557
5558 $class = new TestParentClass();
5559 $ref = new \ReflectionClass($class);
5560
5561 $childAnnotations = $reader->getPropertyAnnotations($ref->getProperty('child'));
5562 $this->assertEquals(1, count($childAnnotations));
5563 $this->assertInstanceOf('Doctrine\Tests\Common\Annotations\Foo\Name', reset($childAnnotations));
5564
5565 $parentAnnotations = $reader->getPropertyAnnotations($ref->getProperty('parent'));
5566 $this->assertEquals(1, count($parentAnnotations));
5567 $this->assertInstanceOf('Doctrine\Tests\Common\Annotations\Bar\Name', reset($parentAnnotations));
5568 }
5569
5570 /**
5571 * @expectedException Doctrine\Common\Annotations\AnnotationException
5572 * @expectedExceptionMessage The annotation "@NameFoo" in property Doctrine\Tests\Common\Annotations\TestAnnotationNotImportedClass::$field was never imported.
5573 */
5574 public function testImportDetectsNotImportedAnnotation()
5575 {
5576 $reader = $this->getReader();
5577 $reader->getPropertyAnnotations(new \ReflectionProperty('Doctrine\Tests\Common\Annotations\TestAnnotationNotImportedClass', 'field'));
5578 }
5579
5580 /**
5581 * @expectedException Doctrine\Common\Annotations\AnnotationException
5582 * @expectedExceptionMessage The annotation "@Foo\Bar\Name" in property Doctrine\Tests\Common\Annotations\TestNonExistentAnnotationClass::$field was never imported.
5583 */
5584 public function testImportDetectsNonExistentAnnotation()
5585 {
5586 $reader = $this->getReader();
5587 $reader->getPropertyAnnotations(new \ReflectionProperty('Doctrine\Tests\Common\Annotations\TestNonExistentAnnotationClass', 'field'));
5588 }
5589
5590 public function testTopLevelAnnotation()
5591 {
5592 $reader = $this->getReader();
5593 $annotations = $reader->getPropertyAnnotations(new \ReflectionProperty('Doctrine\Tests\Common\Annotations\TestTopLevelAnnotationClass', 'field'));
5594
5595 $this->assertEquals(1, count($annotations));
5596 $this->assertInstanceOf('\TopLevelAnnotation', reset($annotations));
5597 }
5598
5599 public function testIgnoresAnnotationsNotPrefixedWithWhitespace()
5600 {
5601 $reader = $this->getReader();
5602
5603 $annotation = $reader->getClassAnnotation(new \ReflectionClass(new TestIgnoresNonAnnotationsClass()), 'Doctrine\Tests\Common\Annotations\Name');
5604 $this->assertInstanceOf('Doctrine\Tests\Common\Annotations\Name', $annotation);
5605 }
5606
5607 /**
5608 * @expectedException Doctrine\Common\Annotations\AnnotationException
5609 * @expectedExceptionMessage The class "Doctrine\Tests\Common\Annotations\Fixtures\NoAnnotation" is not annotated with @Annotation. Are you sure this class can be used as annotation? If so, then you need to add @Annotation to the _class_ doc comment of "Doctrine\Tests\Common\Annotations\Fixtures\NoAnnotation". If it is indeed no annotation, then you need to add @IgnoreAnnotation("NoAnnotation") to the _class_ doc comment of class Doctrine\Tests\Common\Annotations\Fixtures\InvalidAnnotationUsageClass.
5610 */
5611 public function testErrorWhenInvalidAnnotationIsUsed()
5612 {
5613 $reader = $this->getReader();
5614 $ref = new \ReflectionClass('Doctrine\Tests\Common\Annotations\Fixtures\InvalidAnnotationUsageClass');
5615 $reader->getClassAnnotations($ref);
5616 }
5617
5618 public function testInvalidAnnotationUsageButIgnoredClass()
5619 {
5620 $reader = $this->getReader();
5621 $ref = new \ReflectionClass('Doctrine\Tests\Common\Annotations\Fixtures\InvalidAnnotationUsageButIgnoredClass');
5622 $annots = $reader->getClassAnnotations($ref);
5623
5624 $this->assertEquals(2, count($annots));
5625 }
5626
5627 /**
5628 * @group DDC-1660
5629 * @group regression
5630 */
5631 public function testInvalidAnnotationButIgnored()
5632 {
5633 $reader = $this->getReader();
5634 $class = new \ReflectionClass('Doctrine\Tests\Common\Annotations\Fixtures\ClassDDC1660');
5635
5636 $this->assertTrue(class_exists('Doctrine\Tests\Common\Annotations\Fixtures\Annotation\Version'));
5637 $this->assertCount(0, $reader->getClassAnnotations($class));
5638 $this->assertCount(0, $reader->getMethodAnnotations($class->getMethod('bar')));
5639 $this->assertCount(0, $reader->getPropertyAnnotations($class->getProperty('foo')));
5640 }
5641
5642 public function testAnnotationEnumeratorException()
5643 {
5644 $reader = $this->getReader();
5645 $class = new \ReflectionClass('Doctrine\Tests\Common\Annotations\Fixtures\ClassWithAnnotationEnum');
5646
5647 $this->assertCount(1, $bar = $reader->getMethodAnnotations($class->getMethod('bar')));
5648 $this->assertCount(1, $foo = $reader->getPropertyAnnotations($class->getProperty('foo')));
5649
5650 $this->assertInstanceOf('Doctrine\Tests\Common\Annotations\Fixtures\AnnotationEnum', $bar[0]);
5651 $this->assertInstanceOf('Doctrine\Tests\Common\Annotations\Fixtures\AnnotationEnum', $foo[0]);
5652
5653 try {
5654 $reader->getPropertyAnnotations($class->getProperty('invalidProperty'));
5655 $this->fail();
5656 } catch (\Doctrine\Common\Annotations\AnnotationException $exc) {
5657 $this->assertEquals('[Enum Error] Attribute "value" of @Doctrine\Tests\Common\Annotations\Fixtures\AnnotationEnum declared on property Doctrine\Tests\Common\Annotations\Fixtures\ClassWithAnnotationEnum::$invalidProperty accept only [ONE, TWO, THREE], but got FOUR.', $exc->getMessage());
5658 }
5659
5660 try {
5661 $reader->getMethodAnnotations($class->getMethod('invalidMethod'));
5662 $this->fail();
5663 } catch (\Doctrine\Common\Annotations\AnnotationException $exc) {
5664 $this->assertEquals('[Enum Error] Attribute "value" of @Doctrine\Tests\Common\Annotations\Fixtures\AnnotationEnum declared on method Doctrine\Tests\Common\Annotations\Fixtures\ClassWithAnnotationEnum::invalidMethod() accept only [ONE, TWO, THREE], but got 5.', $exc->getMessage());
5665 }
5666 }
5667
5668 /**
5669 * @group DCOM-106
5670 */
5671 public function testIgnoreFixMeAndUpperCaseToDo()
5672 {
5673 $reader = $this->getReader();
5674 $ref = new \ReflectionClass('Doctrine\Tests\Common\Annotations\DCOM106');
5675 $reader->getClassAnnotations($ref);
5676 }
5677
5678 /**
5679 * @return AnnotationReader
5680 */
5681 abstract protected function getReader();
5682 }
5683
5684 /**
5685 * @parseAnnotation("var")
5686 * @author Johannes M. Schmitt <schmittjoh@gmail.com>
5687 *
5688 */
5689 class TestParseAnnotationClass
5690 {
5691 /**
5692 * @var
5693 */
5694 private $field;
5695 }
5696
5697 /**
5698 * @Name
5699 * @author Johannes M. Schmitt <schmittjoh@gmail.com>
5700 */
5701 class TestIgnoresNonAnnotationsClass
5702 {
5703 }
5704
5705 class TestTopLevelAnnotationClass
5706 {
5707 /**
5708 * @\TopLevelAnnotation
5709 */
5710 private $field;
5711 }
5712
5713 class TestNonExistentAnnotationClass
5714 {
5715 /**
5716 * @Foo\Bar\Name
5717 */
5718 private $field;
5719 }
5720
5721 class TestAnnotationNotImportedClass
5722 {
5723 /**
5724 * @NameFoo
5725 */
5726 private $field;
5727 }
5728
5729 class TestChildClass
5730 {
5731 /**
5732 * @\Doctrine\Tests\Common\Annotations\Foo\Name(name = "foo")
5733 */
5734 protected $child;
5735 }
5736
5737 class TestParentClass extends TestChildClass
5738 {
5739 /**
5740 * @\Doctrine\Tests\Common\Annotations\Bar\Name(name = "bar")
5741 */
5742 private $parent;
5743 }
5744
5745 class TestImportWithConcreteAnnotation
5746 {
5747 /**
5748 * @DummyAnnotation(dummyValue = "bar")
5749 */
5750 private $field;
5751 }
5752
5753 /**
5754 * @ignoreAnnotation("var")
5755 */
5756 class DummyClass2 {
5757 /**
5758 * @DummyId @DummyColumn(type="integer") @DummyGeneratedValue
5759 * @var integer
5760 */
5761 private $id;
5762 }
5763
5764 /** @Annotation */
5765 class DummyId extends \Doctrine\Common\Annotations\Annotation {}
5766 /** @Annotation */
5767 class DummyColumn extends \Doctrine\Common\Annotations\Annotation {
5768 public $type;
5769 }
5770 /** @Annotation */
5771 class DummyGeneratedValue extends \Doctrine\Common\Annotations\Annotation {}
5772 /** @Annotation */
5773 class DummyAnnotation extends \Doctrine\Common\Annotations\Annotation {
5774 public $dummyValue;
5775 }
5776
5777 /**
5778 * @api
5779 * @Annotation
5780 */
5781 class DummyAnnotationWithIgnoredAnnotation extends \Doctrine\Common\Annotations\Annotation {
5782 public $dummyValue;
5783 }
5784
5785 /** @Annotation */
5786 class DummyJoinColumn extends \Doctrine\Common\Annotations\Annotation {
5787 public $name;
5788 public $referencedColumnName;
5789 }
5790 /** @Annotation */
5791 class DummyJoinTable extends \Doctrine\Common\Annotations\Annotation {
5792 public $name;
5793 public $joinColumns;
5794 public $inverseJoinColumns;
5795 }
5796
5797 /**
5798 * @DummyAnnotation(@)
5799 */
5800 class DummyClassSyntaxError
5801 {
5802
5803 }
5804
5805 class DummyClassMethodSyntaxError
5806 {
5807 /**
5808 * @DummyAnnotation(@)
5809 */
5810 public function foo()
5811 {
5812
5813 }
5814 }
5815
5816 class DummyClassPropertySyntaxError
5817 {
5818 /**
5819 * @DummyAnnotation(@)
5820 */
5821 public $foo;
5822 }
5823
5824 /**
5825 * @ignoreAnnotation({"since", "var"})
5826 */
5827 class DummyClassNonAnnotationProblem
5828 {
5829 /**
5830 * @DummyAnnotation
5831 *
5832 * @var \Test
5833 * @since 0.1
5834 */
5835 public $foo;
5836 }
5837
5838
5839 /**
5840 * @DummyAnnotation Foo bar <foobar@1domain.com>
5841 */
5842 class DummyClassWithEmail
5843 {
5844
5845 }
5846
5847
5848 /**
5849 * @fixme public
5850 * @TODO
5851 */
5852 class DCOM106
5853 {
5854
5855 }
5856
5857 namespace Doctrine\Tests\Common\Annotations\Foo;
5858
5859 /** @Annotation */
5860 class Name extends \Doctrine\Common\Annotations\Annotation
5861 {
5862 public $name;
5863 }
5864
5865 namespace Doctrine\Tests\Common\Annotations\Bar;
5866
5867 /** @Annotation */
5868 class Name extends \Doctrine\Common\Annotations\Annotation
5869 {
5870 public $name;
5871 }
5872 <?php
5873
5874 namespace Doctrine\Tests\Common\Annotations;
5875
5876 use Doctrine\Common\Annotations\SimpleAnnotationReader;
5877
5878 class SimpleAnnotationReaderTest extends AbstractReaderTest
5879 {
5880 /**
5881 * Contrary to the behavior of the default annotation reader, we do just ignore
5882 * these in the simple annotation reader (so, no expected exception here).
5883 */
5884 public function testImportDetectsNotImportedAnnotation()
5885 {
5886 parent::testImportDetectsNotImportedAnnotation();
5887 }
5888
5889 /**
5890 * Contrary to the behavior of the default annotation reader, we do just ignore
5891 * these in the simple annotation reader (so, no expected exception here).
5892 */
5893 public function testImportDetectsNonExistentAnnotation()
5894 {
5895 parent::testImportDetectsNonExistentAnnotation();
5896 }
5897
5898 /**
5899 * Contrary to the behavior of the default annotation reader, we do just ignore
5900 * these in the simple annotation reader (so, no expected exception here).
5901 */
5902 public function testClassWithInvalidAnnotationTargetAtClassDocBlock()
5903 {
5904 parent::testClassWithInvalidAnnotationTargetAtClassDocBlock();
5905 }
5906
5907 /**
5908 * Contrary to the behavior of the default annotation reader, we do just ignore
5909 * these in the simple annotation reader (so, no expected exception here).
5910 */
5911 public function testClassWithInvalidAnnotationTargetAtPropertyDocBlock()
5912 {
5913 parent::testClassWithInvalidAnnotationTargetAtPropertyDocBlock();
5914 }
5915
5916 /**
5917 * Contrary to the behavior of the default annotation reader, we do just ignore
5918 * these in the simple annotation reader (so, no expected exception here).
5919 */
5920 public function testClassWithInvalidNestedAnnotationTargetAtPropertyDocBlock()
5921 {
5922 parent::testClassWithInvalidNestedAnnotationTargetAtPropertyDocBlock();
5923 }
5924
5925 /**
5926 * Contrary to the behavior of the default annotation reader, we do just ignore
5927 * these in the simple annotation reader (so, no expected exception here).
5928 */
5929 public function testClassWithInvalidAnnotationTargetAtMethodDocBlock()
5930 {
5931 parent::testClassWithInvalidAnnotationTargetAtMethodDocBlock();
5932 }
5933
5934 /**
5935 * @expectedException Doctrine\Common\Annotations\AnnotationException
5936 */
5937 public function testInvalidAnnotationUsageButIgnoredClass()
5938 {
5939 parent::testInvalidAnnotationUsageButIgnoredClass();
5940 }
5941
5942 /**
5943 * @group DDC-1660
5944 * @group regression
5945 *
5946 * Contrary to the behavior of the default annotation reader, @version is not ignored
5947 */
5948 public function testInvalidAnnotationButIgnored()
5949 {
5950 $reader = $this->getReader();
5951 $class = new \ReflectionClass('Doctrine\Tests\Common\Annotations\Fixtures\ClassDDC1660');
5952
5953 $this->assertTrue(class_exists('Doctrine\Tests\Common\Annotations\Fixtures\Annotation\Version'));
5954 $this->assertCount(1, $reader->getClassAnnotations($class));
5955 $this->assertCount(1, $reader->getMethodAnnotations($class->getMethod('bar')));
5956 $this->assertCount(1, $reader->getPropertyAnnotations($class->getProperty('foo')));
5957 }
5958
5959 protected function getReader()
5960 {
5961 $reader = new SimpleAnnotationReader();
5962 $reader->addNamespace(__NAMESPACE__);
5963 $reader->addNamespace(__NAMESPACE__ . '\Fixtures');
5964 $reader->addNamespace(__NAMESPACE__ . '\Fixtures\Annotation');
5965
5966 return $reader;
5967 }
5968 }<?php
5969
5970 namespace Doctrine\Tests\Common\Annotations;
5971
5972 use Doctrine\Tests\Common\Annotations\Fixtures\Annotation\Route;
5973 use Doctrine\Common\Annotations\AnnotationReader;
5974 use Doctrine\Common\Annotations\CachedReader;
5975 use Doctrine\Common\Cache\ArrayCache;
5976
5977 class CachedReaderTest extends AbstractReaderTest
5978 {
5979 private $cache;
5980
5981 public function testIgnoresStaleCache()
5982 {
5983 $file = __DIR__.'/Fixtures/Controller.php';
5984 touch($file);
5985 $name = 'Doctrine\Tests\Common\Annotations\Fixtures\Controller';
5986 $cacheKey = $name.'@[Annot]';
5987
5988 $cache = $this->getMock('Doctrine\Common\Cache\Cache');
5989 $cache
5990 ->expects($this->at(0))
5991 ->method('fetch')
5992 ->with($this->equalTo($cacheKey))
5993 ->will($this->returnValue(array()))
5994 ;
5995 $cache
5996 ->expects($this->at(1))
5997 ->method('fetch')
5998 ->with($this->equalTo('[C]'.$cacheKey))
5999 ->will($this->returnValue(time() - 10))
6000 ;
6001 $cache
6002 ->expects($this->at(2))
6003 ->method('save')
6004 ->with($this->equalTo($cacheKey))
6005 ;
6006 $cache
6007 ->expects($this->at(3))
6008 ->method('save')
6009 ->with($this->equalTo('[C]'.$cacheKey))
6010 ;
6011
6012 $reader = new CachedReader(new AnnotationReader(), $cache, true);
6013 $route = new Route();
6014 $route->pattern = '/someprefix';
6015 $this->assertEquals(array($route), $reader->getClassAnnotations(new \ReflectionClass($name)));
6016 }
6017
6018 protected function getReader()
6019 {
6020 $this->cache = new ArrayCache();
6021 return new CachedReader(new AnnotationReader(), $this->cache);
6022 }
6023 }<?php
6024
6025 namespace Doctrine\Tests\Common\Annotations;
6026
6027 use Doctrine\Common\Annotations\AnnotationReader;
6028
6029 class AnnotationReaderTest extends AbstractReaderTest
6030 {
6031 protected function getReader()
6032 {
6033 return new AnnotationReader();
6034 }
6035 }<?php
6036
6037 namespace Doctrine\Tests\Common\Annotations;
6038
6039 use Doctrine\Common\Annotations\AnnotationReader;
6040 use Doctrine\Common\Annotations\FileCacheReader;
6041
6042 class FileCacheReaderTest extends AbstractReaderTest
6043 {
6044 private $cacheDir;
6045
6046 protected function getReader()
6047 {
6048 $this->cacheDir = sys_get_temp_dir() . "/annotations_". uniqid();
6049 @mkdir($this->cacheDir);
6050 return new FileCacheReader(new AnnotationReader(), $this->cacheDir);
6051 }
6052
6053 public function tearDown()
6054 {
6055 foreach (glob($this->cacheDir.'/*.php') AS $file) {
6056 unlink($file);
6057 }
6058 rmdir($this->cacheDir);
6059 }
6060
6061 /**
6062 * @group DCOM-81
6063 */
6064 public function testAttemptToCreateAnnotationCacheDir()
6065 {
6066 $this->cacheDir = sys_get_temp_dir() . "/not_existed_dir_". uniqid();
6067
6068 $this->assertFalse(is_dir($this->cacheDir));
6069
6070 $cache = new FileCacheReader(new AnnotationReader(), $this->cacheDir);
6071
6072 $this->assertTrue(is_dir($this->cacheDir));
6073 }
6074 }<?php
6075
6076 namespace Doctrine\Tests\Common\Annotations;
6077
6078 use Doctrine\Tests\Common\Annotations\DummyAnnotation;
6079 use Doctrine\Tests\Common\Annotations\Name;
6080 use Doctrine\Tests\Common\Annotations\DummyJoinTable;
6081 use Doctrine\Tests\Common\Annotations\DummyJoinColumn;
6082
6083 /**
6084 * A description of this class.
6085 *
6086 * Let's see if the parser recognizes that this @ is not really referring to an
6087 * annotation. Also make sure that @var \ is not concated to "@var\is".
6088 *
6089 * @author robo
6090 * @since 2.0
6091 * @DummyAnnotation(dummyValue="hello")
6092 */
6093 class DummyClass
6094 {
6095 /**
6096 * A nice property.
6097 *
6098 * @var mixed
6099 * @DummyAnnotation(dummyValue="fieldHello")
6100 */
6101 private $field1;
6102
6103 /**
6104 * @DummyJoinTable(name="join_table",
6105 * joinColumns={@DummyJoinColumn(name="col1", referencedColumnName="col2")},
6106 * inverseJoinColumns={
6107 * @DummyJoinColumn(name="col3", referencedColumnName="col4")
6108 * })
6109 */
6110 private $field2;
6111
6112 /**
6113 * Gets the value of field1.
6114 *
6115 * @return mixed
6116 * @DummyAnnotation({1,2,"three"})
6117 */
6118 public function getField1()
6119 {
6120 }
6121 }
6122 <?php
6123
6124 namespace Doctrine\Tests\Common\Annotations;
6125
6126 use Doctrine\Common\Annotations\FileCacheReader;
6127 use Doctrine\Common\Cache\ArrayCache;
6128 use Doctrine\Common\Annotations\CachedReader;
6129 use Doctrine\Common\Annotations\DocLexer;
6130 use Doctrine\Common\Annotations\DocParser;
6131 use Doctrine\Common\Annotations\PhpParser;
6132 use Doctrine\Common\Annotations\AnnotationReader;
6133
6134 require_once __DIR__ . '/Fixtures/Annotation/Route.php';
6135 require_once __DIR__ . '/Fixtures/Annotation/Template.php';
6136 require_once __DIR__ . '/Fixtures/Annotation/Secure.php';
6137 require_once __DIR__ . '/Fixtures/SingleClassLOC1000.php';
6138
6139 class PerformanceTest extends \PHPUnit_Framework_TestCase
6140 {
6141 /**
6142 * @group performance
6143 */
6144 public function testCachedReadPerformanceWithInMemory()
6145 {
6146 $reader = new CachedReader(new AnnotationReader(), new ArrayCache());
6147 $method = $this->getMethod();
6148
6149 $time = microtime(true);
6150 for ($i=0,$c=500; $i<$c; $i++) {
6151 $reader->getMethodAnnotations($method);
6152 }
6153 $time = microtime(true) - $time;
6154
6155 $this->printResults('cached reader (in-memory)', $time, $c);
6156 }
6157
6158 /**
6159 * @group performance
6160 */
6161 public function testCachedReadPerformanceWithFileCache()
6162 {
6163 $method = $this->getMethod();
6164
6165 // prime cache
6166 $reader = new FileCacheReader(new AnnotationReader(), sys_get_temp_dir());
6167 $reader->getMethodAnnotations($method);
6168
6169 $time = microtime(true);
6170 for ($i=0,$c=500; $i<$c; $i++) {
6171 $reader = new FileCacheReader(new AnnotationReader(), sys_get_temp_dir());
6172 $reader->getMethodAnnotations($method);
6173 clearstatcache();
6174 }
6175 $time = microtime(true) - $time;
6176
6177 $this->printResults('cached reader (file)', $time, $c);
6178 }
6179
6180 /**
6181 * @group performance
6182 */
6183 public function testReadPerformance()
6184 {
6185 $method = $this->getMethod();
6186
6187 $time = microtime(true);
6188 for ($i=0,$c=150; $i<$c; $i++) {
6189 $reader = new AnnotationReader();
6190 $reader->getMethodAnnotations($method);
6191 }
6192 $time = microtime(true) - $time;
6193
6194 $this->printResults('reader', $time, $c);
6195 }
6196
6197 /**
6198 * @group performance
6199 */
6200 public function testDocParsePerformance()
6201 {
6202 $imports = array(
6203 'ignorephpdoc' => 'Annotations\Annotation\IgnorePhpDoc',
6204 'ignoreannotation' => 'Annotations\Annotation\IgnoreAnnotation',
6205 'route' => 'Doctrine\Tests\Common\Annotations\Fixtures\Annotation\Route',
6206 'template' => 'Doctrine\Tests\Common\Annotations\Fixtures\Annotation\Template',
6207 '__NAMESPACE__' => 'Doctrine\Tests\Common\Annotations\Fixtures',
6208 );
6209 $ignored = array(
6210 'access', 'author', 'copyright', 'deprecated', 'example', 'ignore',
6211 'internal', 'link', 'see', 'since', 'tutorial', 'version', 'package',
6212 'subpackage', 'name', 'global', 'param', 'return', 'staticvar',
6213 'static', 'var', 'throws', 'inheritdoc',
6214 );
6215
6216 $method = $this->getMethod();
6217 $methodComment = $method->getDocComment();
6218 $classComment = $method->getDeclaringClass()->getDocComment();
6219
6220 $time = microtime(true);
6221 for ($i=0,$c=200; $i<$c; $i++) {
6222 $parser = new DocParser();
6223 $parser->setImports($imports);
6224 $parser->setIgnoredAnnotationNames($ignored);
6225 $parser->setIgnoreNotImportedAnnotations(true);
6226
6227 $parser->parse($methodComment);
6228 $parser->parse($classComment);
6229 }
6230 $time = microtime(true) - $time;
6231
6232 $this->printResults('doc-parser', $time, $c);
6233 }
6234
6235 /**
6236 * @group performance
6237 */
6238 public function testDocLexerPerformance()
6239 {
6240 $method = $this->getMethod();
6241 $methodComment = $method->getDocComment();
6242 $classComment = $method->getDeclaringClass()->getDocComment();
6243
6244 $time = microtime(true);
6245 for ($i=0,$c=500; $i<$c; $i++) {
6246 $lexer = new DocLexer();
6247 $lexer->setInput($methodComment);
6248 $lexer->setInput($classComment);
6249 }
6250 $time = microtime(true) - $time;
6251
6252 $this->printResults('doc-lexer', $time, $c);
6253 }
6254
6255 /**
6256 * @group performance
6257 */
6258 public function testPhpParserPerformanceWithShortCut()
6259 {
6260 $class = new \ReflectionClass('Doctrine\Tests\Common\Annotations\Fixtures\NamespacedSingleClassLOC1000');
6261
6262 $time = microtime(true);
6263 for ($i=0,$c=500; $i<$c; $i++) {
6264 $parser = new PhpParser();
6265 $parser->parseClass($class);
6266 }
6267 $time = microtime(true) - $time;
6268
6269 $this->printResults('doc-parser-with-short-cut', $time, $c);
6270 }
6271
6272 /**
6273 * @group performance
6274 */
6275 public function testPhpParserPerformanceWithoutShortCut()
6276 {
6277 $class = new \ReflectionClass('SingleClassLOC1000');
6278
6279 $time = microtime(true);
6280 for ($i=0,$c=500; $i<$c; $i++) {
6281 $parser = new PhpParser();
6282 $parser->parseClass($class);
6283 }
6284 $time = microtime(true) - $time;
6285
6286 $this->printResults('doc-parser-without-short-cut', $time, $c);
6287 }
6288
6289 private function getMethod()
6290 {
6291 return new \ReflectionMethod('Doctrine\Tests\Common\Annotations\Fixtures\Controller', 'helloAction');
6292 }
6293
6294 private function printResults($test, $time, $iterations)
6295 {
6296 if (0 == $iterations) {
6297 throw new \InvalidArgumentException('$iterations cannot be zero.');
6298 }
6299
6300 $title = $test." results:\n";
6301 $iterationsText = sprintf("Iterations: %d\n", $iterations);
6302 $totalTime = sprintf("Total Time: %.3f s\n", $time);
6303 $iterationTime = sprintf("Time per iteration: %.3f ms\n", $time/$iterations * 1000);
6304
6305 $max = max(strlen($title), strlen($iterationTime)) - 1;
6306
6307 echo "\n".str_repeat('-', $max)."\n";
6308 echo $title;
6309 echo str_repeat('=', $max)."\n";
6310 echo $iterationsText;
6311 echo $totalTime;
6312 echo $iterationTime;
6313 echo str_repeat('-', $max)."\n";
6314 }
6315 }<?php
6316
6317 namespace Doctrine\Tests\Common\Annotations;
6318
6319 use Doctrine\Common\Annotations\DocLexer;
6320
6321 class DocLexerTest extends \PHPUnit_Framework_TestCase
6322 {
6323 public function testMarkerAnnotation()
6324 {
6325 $lexer = new DocLexer;
6326
6327 $lexer->setInput("@Name");
6328 $this->assertNull($lexer->token);
6329 $this->assertNull($lexer->lookahead);
6330
6331 $this->assertTrue($lexer->moveNext());
6332 $this->assertNull($lexer->token);
6333 $this->assertEquals('@', $lexer->lookahead['value']);
6334
6335 $this->assertTrue($lexer->moveNext());
6336 $this->assertEquals('@', $lexer->token['value']);
6337 $this->assertEquals('Name', $lexer->lookahead['value']);
6338
6339 $this->assertFalse($lexer->moveNext());
6340 }
6341
6342 public function testScannerTokenizesDocBlockWhitConstants()
6343 {
6344 $lexer = new DocLexer();
6345 $docblock = '@AnnotationWithConstants(PHP_EOL, ClassWithConstants::SOME_VALUE, \Doctrine\Tests\Common\Annotations\Fixtures\IntefaceWithConstants::SOME_VALUE)';
6346
6347 $tokens = array (
6348 array(
6349 'value' => '@',
6350 'position' => 0,
6351 'type' => DocLexer::T_AT,
6352 ),
6353 array(
6354 'value' => 'AnnotationWithConstants',
6355 'position' => 1,
6356 'type' => DocLexer::T_IDENTIFIER,
6357 ),
6358 array(
6359 'value' => '(',
6360 'position' => 24,
6361 'type' => DocLexer::T_OPEN_PARENTHESIS,
6362 ),
6363 array(
6364 'value' => 'PHP_EOL',
6365 'position' => 25,
6366 'type' => DocLexer::T_IDENTIFIER,
6367 ),
6368 array(
6369 'value' => ',',
6370 'position' => 32,
6371 'type' => DocLexer::T_COMMA,
6372 ),
6373 array(
6374 'value' => 'ClassWithConstants::SOME_VALUE',
6375 'position' => 34,
6376 'type' => DocLexer::T_IDENTIFIER,
6377 ),
6378 array(
6379 'value' => ',',
6380 'position' => 64,
6381 'type' => DocLexer::T_COMMA,
6382 ),
6383 array(
6384 'value' => '\\Doctrine\\Tests\\Common\\Annotations\\Fixtures\\IntefaceWithConstants::SOME_VALUE',
6385 'position' => 66,
6386 'type' => DocLexer::T_IDENTIFIER,
6387 ),
6388 array(
6389 'value' => ')',
6390 'position' => 143,
6391 'type' => DocLexer::T_CLOSE_PARENTHESIS,
6392 )
6393
6394 );
6395
6396 $lexer->setInput($docblock);
6397
6398 foreach ($tokens as $expected) {
6399 $lexer->moveNext();
6400 $lookahead = $lexer->lookahead;
6401 $this->assertEquals($expected['value'], $lookahead['value']);
6402 $this->assertEquals($expected['type'], $lookahead['type']);
6403 $this->assertEquals($expected['position'], $lookahead['position']);
6404 }
6405
6406 $this->assertFalse($lexer->moveNext());
6407 }
6408
6409
6410 public function testScannerTokenizesDocBlockWhitInvalidIdentifier()
6411 {
6412 $lexer = new DocLexer();
6413 $docblock = '@Foo\3.42';
6414
6415 $tokens = array (
6416 array(
6417 'value' => '@',
6418 'position' => 0,
6419 'type' => DocLexer::T_AT,
6420 ),
6421 array(
6422 'value' => 'Foo',
6423 'position' => 1,
6424 'type' => DocLexer::T_IDENTIFIER,
6425 ),
6426 array(
6427 'value' => '\\',
6428 'position' => 4,
6429 'type' => DocLexer::T_NAMESPACE_SEPARATOR,
6430 ),
6431 array(
6432 'value' => 3.42,
6433 'position' => 5,
6434 'type' => DocLexer::T_FLOAT,
6435 )
6436 );
6437
6438 $lexer->setInput($docblock);
6439
6440 foreach ($tokens as $expected) {
6441 $lexer->moveNext();
6442 $lookahead = $lexer->lookahead;
6443 $this->assertEquals($expected['value'], $lookahead['value']);
6444 $this->assertEquals($expected['type'], $lookahead['type']);
6445 $this->assertEquals($expected['position'], $lookahead['position']);
6446 }
6447
6448 $this->assertFalse($lexer->moveNext());
6449 }
6450
6451 }<?php
6452
6453 namespace Doctrine\Tests\Common\Annotations\Fixtures;
6454
6455 /**
6456 * @Annotation
6457 * @Target("ALL")
6458 * @Attributes({
6459 @Attribute("value", required = true , type = "string"),
6460 @Attribute("annot", required = true , type = "Doctrine\Tests\Common\Annotations\Fixtures\AnnotationTargetAnnotation"),
6461 })
6462 */
6463 final class AnnotationWithRequiredAttributes
6464 {
6465
6466 public final function __construct(array $data)
6467 {
6468 foreach ($data as $key => $value) {
6469 $this->$key = $value;
6470 }
6471 }
6472
6473 /**
6474 * @var string
6475 */
6476 private $value;
6477
6478 /**
6479 *
6480 * @var Doctrine\Tests\Common\Annotations\Fixtures\AnnotationTargetAnnotation
6481 */
6482 private $annot;
6483
6484 /**
6485 * @return string
6486 */
6487 public function getValue()
6488 {
6489 return $this->value;
6490 }
6491
6492 /**
6493 * @return Doctrine\Tests\Common\Annotations\Fixtures\AnnotationTargetAnnotation
6494 */
6495 public function getAnnot()
6496 {
6497 return $this->annot;
6498 }
6499
6500 }<?php
6501
6502 namespace Doctrine\Tests\Common\Annotations\Fixtures;
6503
6504 use Doctrine\Tests\Common\Annotations\Fixtures\AnnotationTargetClass;
6505 use Doctrine\Tests\Common\Annotations\Fixtures\AnnotationTargetAnnotation;
6506
6507 /**
6508 * @AnnotationTargetClass("Some data")
6509 */
6510 class ClassWithInvalidAnnotationTargetAtProperty
6511 {
6512
6513 /**
6514 * @AnnotationTargetClass("Bar")
6515 */
6516 public $foo;
6517
6518
6519 /**
6520 * @AnnotationTargetAnnotation("Foo")
6521 */
6522 public $bar;
6523 }<?php
6524
6525 /**
6526 * This class is not an annotation
6527 * It's a class build to test ClassWithInclude
6528 */
6529 class Api
6530 {
6531
6532 }<?php
6533
6534 namespace Doctrine\Tests\Common\Annotations\Fixtures;
6535
6536 /**
6537 * @Annotation
6538 * @Target("ALL")
6539 */
6540 final class AnnotationWithConstants
6541 {
6542
6543 const INTEGER = 1;
6544 const FLOAT = 1.2;
6545 const STRING = '1.2.3';
6546
6547 /**
6548 * @var mixed
6549 */
6550 public $value;
6551 }<?php
6552
6553 namespace Doctrine\Tests\Common\Annotations\Fixtures;
6554
6555 use
6556 \Doctrine\Tests\Common\Annotations\Fixtures\Annotation\Secure,
6557 \Doctrine\Tests\Common\Annotations\Fixtures\Annotation\Route
6558 ;
6559 use \Doctrine\Tests\Common\Annotations\Fixtures\Annotation\Template;
6560
6561 class ClassWithFullyQualifiedUseStatements {}<?php
6562
6563 namespace Doctrine\Tests\Common\Annotations\Fixtures;
6564
6565 use Doctrine\Tests\Common\Annotations\Fixtures\Annotation\Route;
6566
6567 /**
6568 * @NoAnnotation
6569 * @IgnoreAnnotation("NoAnnotation")
6570 * @Route("foo")
6571 */
6572 class InvalidAnnotationUsageButIgnoredClass
6573 {
6574 }<?php
6575
6576 namespace Doctrine\Tests\Common\Annotations\Fixtures;
6577
6578 use Doctrine\Tests\Common\Annotations\Fixtures\AnnotationWithVarType;
6579 use Doctrine\Tests\Common\Annotations\Fixtures\AnnotationTargetAll;
6580 use Doctrine\Tests\Common\Annotations\Fixtures\AnnotationTargetAnnotation;
6581
6582 class ClassWithAnnotationWithVarType
6583 {
6584 /**
6585 * @AnnotationWithVarType(string = "String Value")
6586 */
6587 public $foo;
6588
6589 /**
6590 * @AnnotationWithVarType(annotation = @AnnotationTargetAll)
6591 */
6592 public function bar(){}
6593
6594
6595 /**
6596 * @AnnotationWithVarType(string = 123)
6597 */
6598 public $invalidProperty;
6599
6600 /**
6601 * @AnnotationWithVarType(annotation = @AnnotationTargetAnnotation)
6602 */
6603 public function invalidMethod(){}
6604 }<?php
6605
6606 namespace Doctrine\Tests\Common\Annotations\Fixtures;
6607
6608 /**
6609 * @Annotation
6610 * @Target("ALL")
6611 */
6612 final class AnnotationWithVarType
6613 {
6614
6615 /**
6616 * @var mixed
6617 */
6618 public $mixed;
6619
6620 /**
6621 * @var boolean
6622 */
6623 public $boolean;
6624
6625 /**
6626 * @var bool
6627 */
6628 public $bool;
6629
6630 /**
6631 * @var float
6632 */
6633 public $float;
6634
6635 /**
6636 * @var string
6637 */
6638 public $string;
6639
6640 /**
6641 * @var integer
6642 */
6643 public $integer;
6644
6645 /**
6646 * @var array
6647 */
6648 public $array;
6649
6650 /**
6651 * @var Doctrine\Tests\Common\Annotations\Fixtures\AnnotationTargetAll
6652 */
6653 public $annotation;
6654
6655 /**
6656 * @var array<integer>
6657 */
6658 public $arrayOfIntegers;
6659
6660 /**
6661 * @var array<Doctrine\Tests\Common\Annotations\Fixtures\AnnotationTargetAll>
6662 */
6663 public $arrayOfAnnotations;
6664
6665 }<?php
6666
6667 namespace Doctrine\Tests\Common\Annotations\Fixtures;
6668
6669 /**
6670 * @since 2.0
6671 * @version $Id: SomeEntityClass.php 509 2012-02-03 09:38:48Z mf $
6672 */
6673 class ClassDDC1660
6674 {
6675
6676 /**
6677 * @var string
6678 * @since 2.0
6679 * @version 1
6680 */
6681 public $foo;
6682
6683 /**
6684 * @param string
6685 * @return string
6686 * @since 2.0
6687 * @version 1
6688 */
6689 public function bar($param)
6690 {
6691 return null;
6692 }
6693
6694 }<?php
6695
6696 use Doctrine\Tests\Common\Annotations\Fixtures\Annotation\Secure;
6697 use Doctrine\Tests\Common\Annotations\Fixtures\Annotation\Route;
6698
6699 class SingleClassLOC1000
6700 {
6701 const TEST1 = 1234567890;
6702 const TEST2 = 1234567890;
6703 const TEST3 = 1234567890;
6704 const TEST4 = 1234567890;
6705 const TEST5 = 1234567890;
6706 const TEST6 = 1234567890;
6707 const TEST7 = 1234567890;
6708 const TEST8 = 1234567890;
6709 const TEST9 = 1234567890;
6710
6711 private $test1 = null;
6712 private $test2 = null;
6713 private $test3 = null;
6714 private $test4 = null;
6715 private $test5 = null;
6716 private $test6 = null;
6717 private $test7 = null;
6718 private $test8 = null;
6719 private $test9 = null;
6720
6721 public function test1()
6722 {
6723 echo $this->test1;
6724 echo $this->test2;
6725 echo $this->test3;
6726 $array = array(1, 2, 3, 4, 5, 6, 7, 8, 9, 10);
6727 foreach ($array as $key => $value) {
6728 echo $key . ' => ' . $value;
6729 }
6730
6731 $val = (string)self::TEST1;
6732 $val .= (string)self::TEST2;
6733 $val .= (string)self::TEST3;
6734 $val .= (string)self::TEST4;
6735 $val .= (string)self::TEST5;
6736 $val .= (string)self::TEST6;
6737 $val .= (string)self::TEST7;
6738 $val .= (string)self::TEST8;
6739 $val .= (string)self::TEST9;
6740
6741 strtolower($val);
6742
6743 return $val;
6744 }
6745
6746 public function test2()
6747 {
6748 echo $this->test1;
6749 echo $this->test2;
6750 echo $this->test3;
6751 $array = array(1, 2, 3, 4, 5, 6, 7, 8, 9, 10);
6752 foreach ($array as $key => $value) {
6753 echo $key . ' => ' . $value;
6754 }
6755
6756 $val = (string)self::TEST1;
6757 $val .= (string)self::TEST2;
6758 $val .= (string)self::TEST3;
6759 $val .= (string)self::TEST4;
6760 $val .= (string)self::TEST5;
6761 $val .= (string)self::TEST6;
6762 $val .= (string)self::TEST7;
6763 $val .= (string)self::TEST8;
6764 $val .= (string)self::TEST9;
6765
6766 strtolower($val);
6767
6768 return $val;
6769 }
6770
6771 public function test3()
6772 {
6773 echo $this->test1;
6774 echo $this->test2;
6775 echo $this->test3;
6776 $array = array(1, 2, 3, 4, 5, 6, 7, 8, 9, 10);
6777 foreach ($array as $key => $value) {
6778 echo $key . ' => ' . $value;
6779 }
6780
6781 $val = (string)self::TEST1;
6782 $val .= (string)self::TEST2;
6783 $val .= (string)self::TEST3;
6784 $val .= (string)self::TEST4;
6785 $val .= (string)self::TEST5;
6786 $val .= (string)self::TEST6;
6787 $val .= (string)self::TEST7;
6788 $val .= (string)self::TEST8;
6789 $val .= (string)self::TEST9;
6790
6791 strtolower($val);
6792
6793 return $val;
6794 }
6795
6796 public function test4()
6797 {
6798 echo $this->test1;
6799 echo $this->test2;
6800 echo $this->test3;
6801 $array = array(1, 2, 3, 4, 5, 6, 7, 8, 9, 10);
6802 foreach ($array as $key => $value) {
6803 echo $key . ' => ' . $value;
6804 }
6805
6806 $val = (string)self::TEST1;
6807 $val .= (string)self::TEST2;
6808 $val .= (string)self::TEST3;
6809 $val .= (string)self::TEST4;
6810 $val .= (string)self::TEST5;
6811 $val .= (string)self::TEST6;
6812 $val .= (string)self::TEST7;
6813 $val .= (string)self::TEST8;
6814 $val .= (string)self::TEST9;
6815
6816 strtolower($val);
6817
6818 return $val;
6819 }
6820
6821 public function test5()
6822 {
6823 echo $this->test1;
6824 echo $this->test2;
6825 echo $this->test3;
6826 $array = array(1, 2, 3, 4, 5, 6, 7, 8, 9, 10);
6827 foreach ($array as $key => $value) {
6828 echo $key . ' => ' . $value;
6829 }
6830
6831 $val = (string)self::TEST1;
6832 $val .= (string)self::TEST2;
6833 $val .= (string)self::TEST3;
6834 $val .= (string)self::TEST4;
6835 $val .= (string)self::TEST5;
6836 $val .= (string)self::TEST6;
6837 $val .= (string)self::TEST7;
6838 $val .= (string)self::TEST8;
6839 $val .= (string)self::TEST9;
6840
6841 strtolower($val);
6842
6843 return $val;
6844 }
6845
6846 public function test6()
6847 {
6848 echo $this->test1;
6849 echo $this->test2;
6850 echo $this->test3;
6851 $array = array(1, 2, 3, 4, 5, 6, 7, 8, 9, 10);
6852 foreach ($array as $key => $value) {
6853 echo $key . ' => ' . $value;
6854 }
6855
6856 $val = (string)self::TEST1;
6857 $val .= (string)self::TEST2;
6858 $val .= (string)self::TEST3;
6859 $val .= (string)self::TEST4;
6860 $val .= (string)self::TEST5;
6861 $val .= (string)self::TEST6;
6862 $val .= (string)self::TEST7;
6863 $val .= (string)self::TEST8;
6864 $val .= (string)self::TEST9;
6865
6866 strtolower($val);
6867
6868 return $val;
6869 }
6870
6871 public function test7()
6872 {
6873 echo $this->test1;
6874 echo $this->test2;
6875 echo $this->test3;
6876 $array = array(1, 2, 3, 4, 5, 6, 7, 8, 9, 10);
6877 foreach ($array as $key => $value) {
6878 echo $key . ' => ' . $value;
6879 }
6880
6881 $val = (string)self::TEST1;
6882 $val .= (string)self::TEST2;
6883 $val .= (string)self::TEST3;
6884 $val .= (string)self::TEST4;
6885 $val .= (string)self::TEST5;
6886 $val .= (string)self::TEST6;
6887 $val .= (string)self::TEST7;
6888 $val .= (string)self::TEST8;
6889 $val .= (string)self::TEST9;
6890
6891 strtolower($val);
6892
6893 return $val;
6894 }
6895
6896 public function test8()
6897 {
6898 echo $this->test1;
6899 echo $this->test2;
6900 echo $this->test3;
6901 $array = array(1, 2, 3, 4, 5, 6, 7, 8, 9, 10);
6902 foreach ($array as $key => $value) {
6903 echo $key . ' => ' . $value;
6904 }
6905
6906 $val = (string)self::TEST1;
6907 $val .= (string)self::TEST2;
6908 $val .= (string)self::TEST3;
6909 $val .= (string)self::TEST4;
6910 $val .= (string)self::TEST5;
6911 $val .= (string)self::TEST6;
6912 $val .= (string)self::TEST7;
6913 $val .= (string)self::TEST8;
6914 $val .= (string)self::TEST9;
6915
6916 strtolower($val);
6917
6918 return $val;
6919
6920 }
6921
6922 public function test9()
6923 {
6924 echo $this->test1;
6925 echo $this->test2;
6926 echo $this->test3;
6927 $array = array(1, 2, 3, 4, 5, 6, 7, 8, 9, 10);
6928 foreach ($array as $key => $value) {
6929 echo $key . ' => ' . $value;
6930 }
6931
6932 $val = (string)self::TEST1;
6933 $val .= (string)self::TEST2;
6934 $val .= (string)self::TEST3;
6935 $val .= (string)self::TEST4;
6936 $val .= (string)self::TEST5;
6937 $val .= (string)self::TEST6;
6938 $val .= (string)self::TEST7;
6939 $val .= (string)self::TEST8;
6940 $val .= (string)self::TEST9;
6941
6942 strtolower($val);
6943
6944 return $val;
6945 }
6946
6947 public function test10()
6948 {
6949 echo $this->test1;
6950 echo $this->test2;
6951 echo $this->test3;
6952 $array = array(1, 2, 3, 4, 5, 6, 7, 8, 9, 10);
6953 foreach ($array as $key => $value) {
6954 echo $key . ' => ' . $value;
6955 }
6956
6957 $val = (string)self::TEST1;
6958 $val .= (string)self::TEST2;
6959 $val .= (string)self::TEST3;
6960 $val .= (string)self::TEST4;
6961 $val .= (string)self::TEST5;
6962 $val .= (string)self::TEST6;
6963 $val .= (string)self::TEST7;
6964 $val .= (string)self::TEST8;
6965 $val .= (string)self::TEST9;
6966
6967 strtolower($val);
6968
6969 return $val;
6970 }
6971
6972 public function test11()
6973 {
6974 echo $this->test1;
6975 echo $this->test2;
6976 echo $this->test3;
6977 $array = array(1, 2, 3, 4, 5, 6, 7, 8, 9, 10);
6978 foreach ($array as $key => $value) {
6979 echo $key . ' => ' . $value;
6980 }
6981
6982 $val = (string)self::TEST1;
6983 $val .= (string)self::TEST2;
6984 $val .= (string)self::TEST3;
6985 $val .= (string)self::TEST4;
6986 $val .= (string)self::TEST5;
6987 $val .= (string)self::TEST6;
6988 $val .= (string)self::TEST7;
6989 $val .= (string)self::TEST8;
6990 $val .= (string)self::TEST9;
6991
6992 strtolower($val);
6993
6994 return $val;
6995 }
6996
6997 public function test12()
6998 {
6999 echo $this->test1;
7000 echo $this->test2;
7001 echo $this->test3;
7002 $array = array(1, 2, 3, 4, 5, 6, 7, 8, 9, 10);
7003 foreach ($array as $key => $value) {
7004 echo $key . ' => ' . $value;
7005 }
7006
7007 $val = (string)self::TEST1;
7008 $val .= (string)self::TEST2;
7009 $val .= (string)self::TEST3;
7010 $val .= (string)self::TEST4;
7011 $val .= (string)self::TEST5;
7012 $val .= (string)self::TEST6;
7013 $val .= (string)self::TEST7;
7014 $val .= (string)self::TEST8;
7015 $val .= (string)self::TEST9;
7016
7017 strtolower($val);
7018
7019 return $val;
7020 }
7021
7022 public function test13()
7023 {
7024 echo $this->test1;
7025 echo $this->test2;
7026 echo $this->test3;
7027 $array = array(1, 2, 3, 4, 5, 6, 7, 8, 9, 10);
7028 foreach ($array as $key => $value) {
7029 echo $key . ' => ' . $value;
7030 }
7031
7032 $val = (string)self::TEST1;
7033 $val .= (string)self::TEST2;
7034 $val .= (string)self::TEST3;
7035 $val .= (string)self::TEST4;
7036 $val .= (string)self::TEST5;
7037 $val .= (string)self::TEST6;
7038 $val .= (string)self::TEST7;
7039 $val .= (string)self::TEST8;
7040 $val .= (string)self::TEST9;
7041
7042 strtolower($val);
7043
7044 return $val;
7045 }
7046
7047 public function test14()
7048 {
7049 echo $this->test1;
7050 echo $this->test2;
7051 echo $this->test3;
7052 $array = array(1, 2, 3, 4, 5, 6, 7, 8, 9, 10);
7053 foreach ($array as $key => $value) {
7054 echo $key . ' => ' . $value;
7055 }
7056
7057 $val = (string)self::TEST1;
7058 $val .= (string)self::TEST2;
7059 $val .= (string)self::TEST3;
7060 $val .= (string)self::TEST4;
7061 $val .= (string)self::TEST5;
7062 $val .= (string)self::TEST6;
7063 $val .= (string)self::TEST7;
7064 $val .= (string)self::TEST8;
7065 $val .= (string)self::TEST9;
7066
7067 strtolower($val);
7068
7069 return $val;
7070 }
7071
7072 public function test15()
7073 {
7074 echo $this->test1;
7075 echo $this->test2;
7076 echo $this->test3;
7077 $array = array(1, 2, 3, 4, 5, 6, 7, 8, 9, 10);
7078 foreach ($array as $key => $value) {
7079 echo $key . ' => ' . $value;
7080 }
7081
7082 $val = (string)self::TEST1;
7083 $val .= (string)self::TEST2;
7084 $val .= (string)self::TEST3;
7085 $val .= (string)self::TEST4;
7086 $val .= (string)self::TEST5;
7087 $val .= (string)self::TEST6;
7088 $val .= (string)self::TEST7;
7089 $val .= (string)self::TEST8;
7090 $val .= (string)self::TEST9;
7091
7092 strtolower($val);
7093
7094 return $val;
7095 }
7096
7097 public function test16()
7098 {
7099 echo $this->test1;
7100 echo $this->test2;
7101 echo $this->test3;
7102 $array = array(1, 2, 3, 4, 5, 6, 7, 8, 9, 10);
7103 foreach ($array as $key => $value) {
7104 echo $key . ' => ' . $value;
7105 }
7106
7107 $val = (string)self::TEST1;
7108 $val .= (string)self::TEST2;
7109 $val .= (string)self::TEST3;
7110 $val .= (string)self::TEST4;
7111 $val .= (string)self::TEST5;
7112 $val .= (string)self::TEST6;
7113 $val .= (string)self::TEST7;
7114 $val .= (string)self::TEST8;
7115 $val .= (string)self::TEST9;
7116
7117 strtolower($val);
7118
7119 return $val;
7120 }
7121
7122 public function test17()
7123 {
7124 echo $this->test1;
7125 echo $this->test2;
7126 echo $this->test3;
7127 $array = array(1, 2, 3, 4, 5, 6, 7, 8, 9, 10);
7128 foreach ($array as $key => $value) {
7129 echo $key . ' => ' . $value;
7130 }
7131
7132 $val = (string)self::TEST1;
7133 $val .= (string)self::TEST2;
7134 $val .= (string)self::TEST3;
7135 $val .= (string)self::TEST4;
7136 $val .= (string)self::TEST5;
7137 $val .= (string)self::TEST6;
7138 $val .= (string)self::TEST7;
7139 $val .= (string)self::TEST8;
7140 $val .= (string)self::TEST9;
7141
7142 strtolower($val);
7143
7144 return $val;
7145
7146 }
7147
7148 public function test18()
7149 {
7150 echo $this->test1;
7151 echo $this->test2;
7152 echo $this->test3;
7153 $array = array(1, 2, 3, 4, 5, 6, 7, 8, 9, 10);
7154 foreach ($array as $key => $value) {
7155 echo $key . ' => ' . $value;
7156 }
7157
7158 $val = (string)self::TEST1;
7159 $val .= (string)self::TEST2;
7160 $val .= (string)self::TEST3;
7161 $val .= (string)self::TEST4;
7162 $val .= (string)self::TEST5;
7163 $val .= (string)self::TEST6;
7164 $val .= (string)self::TEST7;
7165 $val .= (string)self::TEST8;
7166 $val .= (string)self::TEST9;
7167
7168 strtolower($val);
7169
7170 return $val;
7171 }
7172
7173 public function test19()
7174 {
7175 echo $this->test1;
7176 echo $this->test2;
7177 echo $this->test3;
7178 $array = array(1, 2, 3, 4, 5, 6, 7, 8, 9, 10);
7179 foreach ($array as $key => $value) {
7180 echo $key . ' => ' . $value;
7181 }
7182
7183 $val = (string)self::TEST1;
7184 $val .= (string)self::TEST2;
7185 $val .= (string)self::TEST3;
7186 $val .= (string)self::TEST4;
7187 $val .= (string)self::TEST5;
7188 $val .= (string)self::TEST6;
7189 $val .= (string)self::TEST7;
7190 $val .= (string)self::TEST8;
7191 $val .= (string)self::TEST9;
7192
7193 strtolower($val);
7194
7195 return $val;
7196 }
7197
7198 public function test20()
7199 {
7200 echo $this->test1;
7201 echo $this->test2;
7202 echo $this->test3;
7203 $array = array(1, 2, 3, 4, 5, 6, 7, 8, 9, 10);
7204 foreach ($array as $key => $value) {
7205 echo $key . ' => ' . $value;
7206 }
7207
7208 $val = (string)self::TEST1;
7209 $val .= (string)self::TEST2;
7210 $val .= (string)self::TEST3;
7211 $val .= (string)self::TEST4;
7212 $val .= (string)self::TEST5;
7213 $val .= (string)self::TEST6;
7214 $val .= (string)self::TEST7;
7215 $val .= (string)self::TEST8;
7216 $val .= (string)self::TEST9;
7217
7218 strtolower($val);
7219
7220 return $val;
7221 }
7222
7223 public function test21()
7224 {
7225 echo $this->test1;
7226 echo $this->test2;
7227 echo $this->test3;
7228 $array = array(1, 2, 3, 4, 5, 6, 7, 8, 9, 10);
7229 foreach ($array as $key => $value) {
7230 echo $key . ' => ' . $value;
7231 }
7232
7233 $val = (string)self::TEST1;
7234 $val .= (string)self::TEST2;
7235 $val .= (string)self::TEST3;
7236 $val .= (string)self::TEST4;
7237 $val .= (string)self::TEST5;
7238 $val .= (string)self::TEST6;
7239 $val .= (string)self::TEST7;
7240 $val .= (string)self::TEST8;
7241 $val .= (string)self::TEST9;
7242
7243 strtolower($val);
7244
7245 return $val;
7246 }
7247
7248 public function test22()
7249 {
7250 echo $this->test1;
7251 echo $this->test2;
7252 echo $this->test3;
7253 $array = array(1, 2, 3, 4, 5, 6, 7, 8, 9, 10);
7254 foreach ($array as $key => $value) {
7255 echo $key . ' => ' . $value;
7256 }
7257
7258 $val = (string)self::TEST1;
7259 $val .= (string)self::TEST2;
7260 $val .= (string)self::TEST3;
7261 $val .= (string)self::TEST4;
7262 $val .= (string)self::TEST5;
7263 $val .= (string)self::TEST6;
7264 $val .= (string)self::TEST7;
7265 $val .= (string)self::TEST8;
7266 $val .= (string)self::TEST9;
7267
7268 strtolower($val);
7269
7270 return $val;
7271 }
7272
7273 public function test23()
7274 {
7275 echo $this->test1;
7276 echo $this->test2;
7277 echo $this->test3;
7278 $array = array(1, 2, 3, 4, 5, 6, 7, 8, 9, 10);
7279 foreach ($array as $key => $value) {
7280 echo $key . ' => ' . $value;
7281 }
7282
7283 $val = (string)self::TEST1;
7284 $val .= (string)self::TEST2;
7285 $val .= (string)self::TEST3;
7286 $val .= (string)self::TEST4;
7287 $val .= (string)self::TEST5;
7288 $val .= (string)self::TEST6;
7289 $val .= (string)self::TEST7;
7290 $val .= (string)self::TEST8;
7291 $val .= (string)self::TEST9;
7292
7293 strtolower($val);
7294
7295 return $val;
7296 }
7297
7298 public function test24()
7299 {
7300 echo $this->test1;
7301 echo $this->test2;
7302 echo $this->test3;
7303 $array = array(1, 2, 3, 4, 5, 6, 7, 8, 9, 10);
7304 foreach ($array as $key => $value) {
7305 echo $key . ' => ' . $value;
7306 }
7307
7308 $val = (string)self::TEST1;
7309 $val .= (string)self::TEST2;
7310 $val .= (string)self::TEST3;
7311 $val .= (string)self::TEST4;
7312 $val .= (string)self::TEST5;
7313 $val .= (string)self::TEST6;
7314 $val .= (string)self::TEST7;
7315 $val .= (string)self::TEST8;
7316 $val .= (string)self::TEST9;
7317
7318 strtolower($val);
7319
7320 return $val;
7321 }
7322
7323 public function test25()
7324 {
7325 echo $this->test1;
7326 echo $this->test2;
7327 echo $this->test3;
7328 $array = array(1, 2, 3, 4, 5, 6, 7, 8, 9, 10);
7329 foreach ($array as $key => $value) {
7330 echo $key . ' => ' . $value;
7331 }
7332
7333 $val = (string)self::TEST1;
7334 $val .= (string)self::TEST2;
7335 $val .= (string)self::TEST3;
7336 $val .= (string)self::TEST4;
7337 $val .= (string)self::TEST5;
7338 $val .= (string)self::TEST6;
7339 $val .= (string)self::TEST7;
7340 $val .= (string)self::TEST8;
7341 $val .= (string)self::TEST9;
7342
7343 strtolower($val);
7344
7345 return $val;
7346 }
7347
7348 public function test26()
7349 {
7350 echo $this->test1;
7351 echo $this->test2;
7352 echo $this->test3;
7353 $array = array(1, 2, 3, 4, 5, 6, 7, 8, 9, 10);
7354 foreach ($array as $key => $value) {
7355 echo $key . ' => ' . $value;
7356 }
7357
7358 $val = (string)self::TEST1;
7359 $val .= (string)self::TEST2;
7360 $val .= (string)self::TEST3;
7361 $val .= (string)self::TEST4;
7362 $val .= (string)self::TEST5;
7363 $val .= (string)self::TEST6;
7364 $val .= (string)self::TEST7;
7365 $val .= (string)self::TEST8;
7366 $val .= (string)self::TEST9;
7367
7368 strtolower($val);
7369
7370 return $val;
7371 }
7372
7373 public function test27()
7374 {
7375 echo $this->test1;
7376 echo $this->test2;
7377 echo $this->test3;
7378 $array = array(1, 2, 3, 4, 5, 6, 7, 8, 9, 10);
7379 foreach ($array as $key => $value) {
7380 echo $key . ' => ' . $value;
7381 }
7382
7383 $val = (string)self::TEST1;
7384 $val .= (string)self::TEST2;
7385 $val .= (string)self::TEST3;
7386 $val .= (string)self::TEST4;
7387 $val .= (string)self::TEST5;
7388 $val .= (string)self::TEST6;
7389 $val .= (string)self::TEST7;
7390 $val .= (string)self::TEST8;
7391 $val .= (string)self::TEST9;
7392
7393 strtolower($val);
7394
7395 return $val;
7396
7397 }
7398
7399 public function test28()
7400 {
7401 echo $this->test1;
7402 echo $this->test2;
7403 echo $this->test3;
7404 $array = array(1, 2, 3, 4, 5, 6, 7, 8, 9, 10);
7405 foreach ($array as $key => $value) {
7406 echo $key . ' => ' . $value;
7407 }
7408
7409 $val = (string)self::TEST1;
7410 $val .= (string)self::TEST2;
7411 $val .= (string)self::TEST3;
7412 $val .= (string)self::TEST4;
7413 $val .= (string)self::TEST5;
7414 $val .= (string)self::TEST6;
7415 $val .= (string)self::TEST7;
7416 $val .= (string)self::TEST8;
7417 $val .= (string)self::TEST9;
7418
7419 strtolower($val);
7420
7421 return $val;
7422 }
7423
7424 public function test29()
7425 {
7426 echo $this->test1;
7427 echo $this->test2;
7428 echo $this->test3;
7429 $array = array(1, 2, 3, 4, 5, 6, 7, 8, 9, 10);
7430 foreach ($array as $key => $value) {
7431 echo $key . ' => ' . $value;
7432 }
7433
7434 $val = (string)self::TEST1;
7435 $val .= (string)self::TEST2;
7436 $val .= (string)self::TEST3;
7437 $val .= (string)self::TEST4;
7438 $val .= (string)self::TEST5;
7439 $val .= (string)self::TEST6;
7440 $val .= (string)self::TEST7;
7441 $val .= (string)self::TEST8;
7442 $val .= (string)self::TEST9;
7443
7444 strtolower($val);
7445
7446 return $val;
7447 }
7448
7449 public function test30()
7450 {
7451 echo $this->test1;
7452 echo $this->test2;
7453 echo $this->test3;
7454 $array = array(1, 2, 3, 4, 5, 6, 7, 8, 9, 10);
7455 foreach ($array as $key => $value) {
7456 echo $key . ' => ' . $value;
7457 }
7458
7459 $val = (string)self::TEST1;
7460 $val .= (string)self::TEST2;
7461 $val .= (string)self::TEST3;
7462 $val .= (string)self::TEST4;
7463 $val .= (string)self::TEST5;
7464 $val .= (string)self::TEST6;
7465 $val .= (string)self::TEST7;
7466 $val .= (string)self::TEST8;
7467 $val .= (string)self::TEST9;
7468
7469 strtolower($val);
7470
7471 return $val;
7472 }
7473
7474 public function test31()
7475 {
7476 echo $this->test1;
7477 echo $this->test2;
7478 echo $this->test3;
7479 $array = array(1, 2, 3, 4, 5, 6, 7, 8, 9, 10);
7480 foreach ($array as $key => $value) {
7481 echo $key . ' => ' . $value;
7482 }
7483
7484 $val = (string)self::TEST1;
7485 $val .= (string)self::TEST2;
7486 $val .= (string)self::TEST3;
7487 $val .= (string)self::TEST4;
7488 $val .= (string)self::TEST5;
7489 $val .= (string)self::TEST6;
7490 $val .= (string)self::TEST7;
7491 $val .= (string)self::TEST8;
7492 $val .= (string)self::TEST9;
7493
7494 strtolower($val);
7495
7496 return $val;
7497 }
7498
7499 public function test32()
7500 {
7501 echo $this->test1;
7502 echo $this->test2;
7503 echo $this->test3;
7504 $array = array(1, 2, 3, 4, 5, 6, 7, 8, 9, 10);
7505 foreach ($array as $key => $value) {
7506 echo $key . ' => ' . $value;
7507 }
7508
7509 $val = (string)self::TEST1;
7510 $val .= (string)self::TEST2;
7511 $val .= (string)self::TEST3;
7512 $val .= (string)self::TEST4;
7513 $val .= (string)self::TEST5;
7514 $val .= (string)self::TEST6;
7515 $val .= (string)self::TEST7;
7516 $val .= (string)self::TEST8;
7517 $val .= (string)self::TEST9;
7518
7519 strtolower($val);
7520
7521 return $val;
7522 }
7523
7524 public function test33()
7525 {
7526 echo $this->test1;
7527 echo $this->test2;
7528 echo $this->test3;
7529 $array = array(1, 2, 3, 4, 5, 6, 7, 8, 9, 10);
7530 foreach ($array as $key => $value) {
7531 echo $key . ' => ' . $value;
7532 }
7533
7534 $val = (string)self::TEST1;
7535 $val .= (string)self::TEST2;
7536 $val .= (string)self::TEST3;
7537 $val .= (string)self::TEST4;
7538 $val .= (string)self::TEST5;
7539 $val .= (string)self::TEST6;
7540 $val .= (string)self::TEST7;
7541 $val .= (string)self::TEST8;
7542 $val .= (string)self::TEST9;
7543
7544 strtolower($val);
7545
7546 return $val;
7547 }
7548
7549 public function test34()
7550 {
7551 echo $this->test1;
7552 echo $this->test2;
7553 echo $this->test3;
7554 $array = array(1, 2, 3, 4, 5, 6, 7, 8, 9, 10);
7555 foreach ($array as $key => $value) {
7556 echo $key . ' => ' . $value;
7557 }
7558
7559 $val = (string)self::TEST1;
7560 $val .= (string)self::TEST2;
7561 $val .= (string)self::TEST3;
7562 $val .= (string)self::TEST4;
7563 $val .= (string)self::TEST5;
7564 $val .= (string)self::TEST6;
7565 $val .= (string)self::TEST7;
7566 $val .= (string)self::TEST8;
7567 $val .= (string)self::TEST9;
7568
7569 strtolower($val);
7570
7571 return $val;
7572 }
7573
7574 public function test35()
7575 {
7576 echo $this->test1;
7577 echo $this->test2;
7578 echo $this->test3;
7579 $array = array(1, 2, 3, 4, 5, 6, 7, 8, 9, 10);
7580 foreach ($array as $key => $value) {
7581 echo $key . ' => ' . $value;
7582 }
7583
7584 $val = (string)self::TEST1;
7585 $val .= (string)self::TEST2;
7586 $val .= (string)self::TEST3;
7587 $val .= (string)self::TEST4;
7588 $val .= (string)self::TEST5;
7589 $val .= (string)self::TEST6;
7590 $val .= (string)self::TEST7;
7591 $val .= (string)self::TEST8;
7592 $val .= (string)self::TEST9;
7593
7594 strtolower($val);
7595
7596 return $val;
7597 }
7598
7599 public function test36()
7600 {
7601 echo $this->test1;
7602 echo $this->test2;
7603 echo $this->test3;
7604 $array = array(1, 2, 3, 4, 5, 6, 7, 8, 9, 10);
7605 foreach ($array as $key => $value) {
7606 echo $key . ' => ' . $value;
7607 }
7608
7609 $val = (string)self::TEST1;
7610 $val .= (string)self::TEST2;
7611 $val .= (string)self::TEST3;
7612 $val .= (string)self::TEST4;
7613 $val .= (string)self::TEST5;
7614 $val .= (string)self::TEST6;
7615 $val .= (string)self::TEST7;
7616 $val .= (string)self::TEST8;
7617 $val .= (string)self::TEST9;
7618
7619 strtolower($val);
7620
7621 return $val;
7622 }
7623
7624 public function test37()
7625 {
7626 echo $this->test1;
7627 echo $this->test2;
7628 echo $this->test3;
7629 $array = array(1, 2, 3, 4, 5, 6, 7, 8, 9, 10);
7630 foreach ($array as $key => $value) {
7631 echo $key . ' => ' . $value;
7632 }
7633
7634 $val = (string)self::TEST1;
7635 $val .= (string)self::TEST2;
7636 $val .= (string)self::TEST3;
7637 $val .= (string)self::TEST4;
7638 $val .= (string)self::TEST5;
7639 $val .= (string)self::TEST6;
7640 $val .= (string)self::TEST7;
7641 $val .= (string)self::TEST8;
7642 $val .= (string)self::TEST9;
7643
7644 strtolower($val);
7645
7646 return $val;
7647
7648 }
7649
7650 public function test38()
7651 {
7652 echo $this->test1;
7653 echo $this->test2;
7654 echo $this->test3;
7655 $array = array(1, 2, 3, 4, 5, 6, 7, 8, 9, 10);
7656 foreach ($array as $key => $value) {
7657 echo $key . ' => ' . $value;
7658 }
7659
7660 $val = (string)self::TEST1;
7661 $val .= (string)self::TEST2;
7662 $val .= (string)self::TEST3;
7663 $val .= (string)self::TEST4;
7664 $val .= (string)self::TEST5;
7665 $val .= (string)self::TEST6;
7666 $val .= (string)self::TEST7;
7667 $val .= (string)self::TEST8;
7668 $val .= (string)self::TEST9;
7669
7670 strtolower($val);
7671
7672 return $val;
7673 }
7674
7675 public function test39()
7676 {
7677 echo $this->test1;
7678 echo $this->test2;
7679 echo $this->test3;
7680 $array = array(1, 2, 3, 4, 5, 6, 7, 8, 9, 10);
7681 foreach ($array as $key => $value) {
7682 echo $key . ' => ' . $value;
7683 }
7684
7685 $val = (string)self::TEST1;
7686 $val .= (string)self::TEST2;
7687 $val .= (string)self::TEST3;
7688 $val .= (string)self::TEST4;
7689 $val .= (string)self::TEST5;
7690 $val .= (string)self::TEST6;
7691 $val .= (string)self::TEST7;
7692 $val .= (string)self::TEST8;
7693 $val .= (string)self::TEST9;
7694
7695 strtolower($val);
7696
7697 return $val;
7698 }
7699 }<?php
7700
7701 namespace Doctrine\Tests\Common\Annotations\Fixtures;
7702
7703 use
7704 Doctrine\Tests\Common\Annotations\Fixtures\Annotation\Route,
7705 Doctrine\Tests\Common\Annotations\Fixtures\Annotation\Secure
7706 ;
7707
7708 class MultipleImportsInUseStatement {}<?php
7709
7710 namespace {
7711 use Doctrine\Tests\Common\Annotations\Fixtures\Annotation\Secure;
7712 }
7713
7714 namespace {
7715 use Doctrine\Tests\Common\Annotations\Fixtures\Annotation\Route;
7716 use Doctrine\Tests\Common\Annotations\Fixtures\Annotation\Template;
7717
7718 class GlobalNamespacesPerFileWithClassAsLast {}
7719 }<?php
7720
7721 namespace Doctrine\Tests\Common\Annotations\Fixtures;
7722
7723 use Doctrine\Tests\Common\Annotations\Fixtures\Annotation\Secure;
7724 use Doctrine\Tests\Common\Annotations\Fixtures\Annotation\Route;
7725
7726 class EqualNamespacesPerFileWithClassAsFirst {}
7727
7728
7729 namespace Doctrine\Tests\Common\Annotations\Fixtures;
7730
7731 use Doctrine\Tests\Common\Annotations\Fixtures\Annotation\Template;<?php
7732
7733 namespace Doctrine\Tests\Common\Annotations\Fixtures;
7734
7735
7736 /**
7737 * @Annotation
7738 * @Target("METHOD")
7739 */
7740 final class AnnotationTargetMethod
7741 {
7742 public $data;
7743 public $name;
7744 public $target;
7745 }<?php
7746
7747 namespace Doctrine\Tests\Common\Annotations\Fixtures;
7748
7749 use Doctrine\Tests\Common\Annotations\Fixtures\AnnotationEnum;
7750
7751 class ClassWithAnnotationEnum
7752 {
7753 /**
7754 * @AnnotationEnum(AnnotationEnum::ONE)
7755 */
7756 public $foo;
7757
7758 /**
7759 * @AnnotationEnum("TWO")
7760 */
7761 public function bar(){}
7762
7763
7764 /**
7765 * @AnnotationEnum("FOUR")
7766 */
7767 public $invalidProperty;
7768
7769 /**
7770 * @AnnotationEnum(5)
7771 */
7772 public function invalidMethod(){}
7773 }<?php
7774
7775 namespace Doctrine\Tests\Common\Annotations\Fixtures;
7776
7777 /**
7778 * @NoAnnotation
7779 */
7780 class InvalidAnnotationUsageClass
7781 {
7782 }<?php
7783
7784 namespace Doctrine\Tests\Common\Annotations\Fixtures;
7785
7786 class NoAnnotation {}<?php
7787
7788 namespace Doctrine\Tests\Common\Annotations\Fixtures;
7789
7790 /**
7791 * @Annotation
7792 * @Target("ALL")
7793 */
7794 final class AnnotationEnum
7795 {
7796 const ONE = 'ONE';
7797 const TWO = 'TWO';
7798 const THREE = 'THREE';
7799
7800 /**
7801 * @var mixed
7802 *
7803 * @Enum({"ONE","TWO","THREE"})
7804 */
7805 public $value;
7806 }<?php
7807
7808 namespace Doctrine\Tests\Common\Annotations\Fixtures;
7809
7810 /**
7811 * @Annotation
7812 * @Target("ALL")
7813 * @Attributes({
7814 @Attribute("mixed", type = "mixed"),
7815 @Attribute("boolean", type = "boolean"),
7816 @Attribute("bool", type = "bool"),
7817 @Attribute("float", type = "float"),
7818 @Attribute("string", type = "string"),
7819 @Attribute("integer", type = "integer"),
7820 @Attribute("array", type = "array"),
7821 @Attribute("arrayOfIntegers", type = "array<integer>"),
7822 @Attribute("annotation", type = "Doctrine\Tests\Common\Annotations\Fixtures\AnnotationTargetAll"),
7823 @Attribute("arrayOfAnnotations", type = "array<Doctrine\Tests\Common\Annotations\Fixtures\AnnotationTargetAll>"),
7824 })
7825 */
7826 final class AnnotationWithAttributes
7827 {
7828
7829 public final function __construct(array $data)
7830 {
7831 foreach ($data as $key => $value) {
7832 $this->$key = $value;
7833 }
7834 }
7835
7836 private $mixed;
7837 private $boolean;
7838 private $bool;
7839 private $float;
7840 private $string;
7841 private $integer;
7842 private $array;
7843 private $annotation;
7844 private $arrayOfIntegers;
7845 private $arrayOfAnnotations;
7846
7847 /**
7848 * @return mixed
7849 */
7850 public function getMixed()
7851 {
7852 return $this->mixed;
7853 }
7854
7855 /**
7856 * @return boolean
7857 */
7858 public function getBoolean()
7859 {
7860 return $this->boolean;
7861 }
7862
7863 /**
7864 * @return bool
7865 */
7866 public function getBool()
7867 {
7868 return $this->bool;
7869 }
7870
7871 /**
7872 * @return float
7873 */
7874 public function getFloat()
7875 {
7876 return $this->float;
7877 }
7878
7879 /**
7880 * @return string
7881 */
7882 public function getString()
7883 {
7884 return $this->string;
7885 }
7886
7887 public function getInteger()
7888 {
7889 return $this->integer;
7890 }
7891
7892 /**
7893 * @return array
7894 */
7895 public function getArray()
7896 {
7897 return $this->array;
7898 }
7899
7900 /**
7901 * @return Doctrine\Tests\Common\Annotations\Fixtures\AnnotationTargetAll
7902 */
7903 public function getAnnotation()
7904 {
7905 return $this->annotation;
7906 }
7907
7908 /**
7909 * @return array<integer>
7910 */
7911 public function getArrayOfIntegers()
7912 {
7913 return $this->arrayOfIntegers;
7914 }
7915
7916 /**
7917 * @return array<Doctrine\Tests\Common\Annotations\Fixtures\AnnotationTargetAll>
7918 */
7919 public function getArrayOfAnnotations()
7920 {
7921 return $this->arrayOfAnnotations;
7922 }
7923
7924 }<?php
7925
7926 namespace Doctrine\Tests\Common\Annotations\Fixtures;
7927
7928 /**
7929 * @Annotation
7930 * @Target({ "ANNOTATION" })
7931 */
7932 final class AnnotationTargetAnnotation
7933 {
7934 public $data;
7935 public $name;
7936 public $target;
7937 }<?php
7938
7939 namespace Doctrine\Tests\Common\Annotations\Fixtures;
7940
7941 use Doctrine\Tests\Common\Annotations\Fixtures\Annotation\Secure;
7942
7943 namespace Doctrine\Tests\Common\Annotations\Fixtures;
7944
7945 use Doctrine\Tests\Common\Annotations\Fixtures\Annotation\Route;
7946 use Doctrine\Tests\Common\Annotations\Fixtures\Annotation\Template;
7947
7948 class EqualNamespacesPerFileWithClassAsLast {}<?php
7949
7950 namespace Doctrine\Tests\Common\Annotations\Fixtures;
7951
7952 /**
7953 * @Annotation
7954 * @Target(@)
7955 */
7956 final class AnnotationWithTargetSyntaxError
7957 {
7958 }<?php
7959
7960 namespace Doctrine\Tests\Common\Annotations\Fixtures;
7961
7962 /**
7963 * @Annotation
7964 * @Target("ALL")
7965 */
7966 final class AnnotationWithRequiredAttributesWithoutContructor
7967 {
7968
7969 /**
7970 * @Required
7971 * @var string
7972 */
7973 public $value;
7974
7975 /**
7976 * @Required
7977 * @var Doctrine\Tests\Common\Annotations\Fixtures\AnnotationTargetAnnotation
7978 */
7979 public $annot;
7980
7981 }<?php
7982
7983 namespace Doctrine\Tests\Common\Annotations\Fixtures;
7984
7985
7986 /**
7987 * @Annotation
7988 * @Target("CLASS")
7989 */
7990 final class AnnotationTargetClass
7991 {
7992 public $data;
7993 public $name;
7994 public $target;
7995 }<?php
7996
7997 namespace Doctrine\Tests\Common\Annotations\Fixtures;
7998
7999 use Doctrine\Tests\Common\Annotations\Fixtures\AnnotationTargetClass;
8000 use Doctrine\Tests\Common\Annotations\Fixtures\AnnotationTargetAll;
8001 use Doctrine\Tests\Common\Annotations\Fixtures\AnnotationTargetPropertyMethod;
8002 use Doctrine\Tests\Common\Annotations\Fixtures\AnnotationTargetNestedAnnotation;
8003
8004 /**
8005 * @AnnotationTargetClass("Some data")
8006 */
8007 class ClassWithValidAnnotationTarget
8008 {
8009
8010 /**
8011 * @AnnotationTargetPropertyMethod("Some data")
8012 */
8013 public $foo;
8014
8015
8016 /**
8017 * @AnnotationTargetAll("Some data",name="Some name")
8018 */
8019 public $name;
8020
8021 /**
8022 * @AnnotationTargetPropertyMethod("Some data",name="Some name")
8023 */
8024 public function someFunction()
8025 {
8026
8027 }
8028
8029
8030 /**
8031 * @AnnotationTargetAll(@AnnotationTargetAnnotation)
8032 */
8033 public $nested;
8034
8035 }<?php
8036
8037 namespace Doctrine\Tests\Common\Annotations\Fixtures;
8038
8039 /**
8040 * @Annotation
8041 * @Target("ALL")
8042 */
8043 final class AnnotationEnumLiteralInvalid
8044 {
8045 const ONE = 1;
8046 const TWO = 2;
8047 const THREE = 3;
8048
8049 /**
8050 * @var mixed
8051 *
8052 * @Enum(
8053 * value = {
8054 * 1,
8055 * 2
8056 * },
8057 * literal = {
8058 * 1 : "AnnotationEnumLiteral::ONE",
8059 * 2 : "AnnotationEnumLiteral::TWO",
8060 * 3 : "AnnotationEnumLiteral::THREE"
8061 * }
8062 * )
8063 */
8064 public $value;
8065 }<?php
8066
8067 namespace Doctrine\Tests\Common\Annotations\Fixtures;
8068
8069 use Doctrine\Tests\Common\Annotations\Fixtures\Annotation\Secure;
8070 use Doctrine\Tests\Common\Annotations\Fixtures\Annotation\Route;
8071
8072 class AnotherClass { }
8073 class MultipleClassesInFile { }
8074 <?php
8075
8076 namespace Doctrine\Tests\Common\Annotations\Fixtures;
8077
8078 /**
8079 * @Annotation
8080 * @Target("ALL")
8081 */
8082 class AnnotationTargetAll
8083 {
8084 public $data;
8085 public $name;
8086 public $target;
8087 }<?php
8088
8089 namespace Doctrine\Tests\Common\Annotations\Fixtures;
8090
8091 // Include a class named Api
8092 require_once(__DIR__ . '/Api.php');
8093
8094 use Doctrine\Tests\Common\Annotations\DummyAnnotationWithIgnoredAnnotation;
8095
8096 /**
8097 * @DummyAnnotationWithIgnoredAnnotation(dummyValue="hello")
8098 */
8099 class ClassWithRequire
8100 {
8101 }<?php
8102
8103 // namespace Doctrine\Tests\Common\Annotations\Fixtures;
8104 namespace Doctrine\Tests\Common\Annotations\Fixtures\Foo {
8105
8106 use Doctrine\Tests\Common\Annotations\Fixtures\Annotation\Secure;
8107
8108 // class NamespaceAndClassCommentedOut {}
8109 }
8110
8111 namespace Doctrine\Tests\Common\Annotations\Fixtures {
8112
8113 // class NamespaceAndClassCommentedOut {}
8114 use Doctrine\Tests\Common\Annotations\Fixtures\Annotation\Route;
8115
8116 // namespace Doctrine\Tests\Common\Annotations\Fixtures;
8117 use Doctrine\Tests\Common\Annotations\Fixtures\Annotation\Template;
8118
8119 class NamespaceAndClassCommentedOut {}
8120 }<?php
8121
8122 namespace Doctrine\Tests\Common\Annotations\Fixtures;
8123
8124 use Doctrine\Tests\Common\Annotations\Fixtures\Annotation\Secure;
8125 use Doctrine\Tests\Common\Annotations\Fixtures\Annotation\Route;
8126 use Doctrine\Tests\Common\Annotations\Fixtures\Annotation\Template;
8127
8128 $var = 1;
8129 function () use ($var) {};
8130
8131 class NamespaceWithClosureDeclaration {}
8132 <?php
8133
8134 namespace Doctrine\Tests\Common\Annotations\Fixtures;
8135
8136 use Doctrine\Tests\Common\Annotations\Fixtures\AnnotationWithTargetSyntaxError;
8137
8138 /**
8139 * @AnnotationWithTargetSyntaxError()
8140 */
8141 class ClassWithAnnotationWithTargetSyntaxError
8142 {
8143 /**
8144 * @AnnotationWithTargetSyntaxError()
8145 */
8146 public $foo;
8147
8148 /**
8149 * @AnnotationWithTargetSyntaxError()
8150 */
8151 public function bar(){}
8152 }<?php
8153
8154 namespace Doctrine\Tests\Common\Annotations\Fixtures;
8155
8156 interface IntefaceWithConstants
8157 {
8158
8159 const SOME_VALUE = 'IntefaceWithConstants.SOME_VALUE';
8160 const SOME_KEY = 'IntefaceWithConstants.SOME_KEY';
8161 }<?php
8162
8163 use Doctrine\Tests\Common\Annotations\Fixtures\Annotation\Template;
8164 use Doctrine\Tests\Common\Annotations\Fixtures\Annotation\Route;
8165
8166 /**
8167 * @Route("foo")
8168 * @Template
8169 */
8170 class AnnotationsTestsFixturesNonNamespacedClass { }<?php
8171
8172 namespace Doctrine\Tests\Common\Annotations\Fixtures;
8173
8174 use Doctrine\Tests\Common\Annotations\Fixtures\AnnotationTargetPropertyMethod;
8175
8176 /**
8177 * @AnnotationTargetPropertyMethod("Some data")
8178 */
8179 class ClassWithInvalidAnnotationTargetAtClass
8180 {
8181
8182 /**
8183 * @AnnotationTargetPropertyMethod("Bar")
8184 */
8185 public $foo;
8186 }<?php
8187
8188 namespace Doctrine\Tests\Common\Annotations\Fixtures;
8189
8190 use Doctrine\Tests\Common\Annotations\Fixtures\AnnotationEnumLiteral as SelfEnum;
8191
8192 /**
8193 * @Annotation
8194 * @Target("ALL")
8195 */
8196 final class AnnotationEnumLiteral
8197 {
8198 const ONE = 1;
8199 const TWO = 2;
8200 const THREE = 3;
8201
8202 /**
8203 * @var mixed
8204 *
8205 * @Enum(
8206 * value = {
8207 * 1,
8208 * 2,
8209 * 3,
8210 * },
8211 * literal = {
8212 * 1 : "AnnotationEnumLiteral::ONE",
8213 * 2 : "AnnotationEnumLiteral::TWO",
8214 * 3 : "AnnotationEnumLiteral::THREE",
8215 * }
8216 * )
8217 */
8218 public $value;
8219 }<?php
8220
8221 namespace Doctrine\Tests\Common\Annotations\Fixtures;
8222
8223 use Doctrine\Tests\Common\Annotations\Fixtures\AnnotationTargetClass;
8224
8225 /**
8226 * @AnnotationTargetClass("Some data")
8227 */
8228 class ClassWithInvalidAnnotationTargetAtMethod
8229 {
8230
8231 /**
8232 * @AnnotationTargetClass("functionName")
8233 */
8234 public function functionName($param)
8235 {
8236
8237 }
8238 }<?php
8239
8240 namespace Doctrine\Tests\Common\Annotations\Fixtures;
8241
8242 class ClassWithConstants
8243 {
8244
8245 const SOME_VALUE = 'ClassWithConstants.SOME_VALUE';
8246 const SOME_KEY = 'ClassWithConstants.SOME_KEY';
8247 }<?php
8248
8249 namespace Doctrine\Tests\Common\Annotations\Fixtures;
8250
8251 use Doctrine\Tests\Common\Annotations\Fixtures\AnnotationTargetAll;
8252 use Doctrine\Tests\Common\Annotations\Fixtures\AnnotationTargetAnnotation;
8253
8254 /**
8255 * @AnnotationTargetAll("Foo")
8256 */
8257 final class ClassWithClosure
8258 {
8259
8260 /**
8261 * @AnnotationTargetAll(@AnnotationTargetAnnotation)
8262 * @var string
8263 */
8264 public $value;
8265
8266 /**
8267 * @AnnotationTargetAll(@AnnotationTargetAnnotation)
8268 *
8269 * @param \Closure $callback
8270 * @return \Closure
8271 */
8272 public function methodName(\Closure $callback)
8273 {
8274 $self = $this;
8275 return function() use ($self, $callback) {
8276 return $callback;
8277 };
8278 }
8279
8280 /**
8281 * @param integer $year
8282 * @param integer $month
8283 * @param integer $day
8284 * @return \Doctrine\Common\Collections\ArrayCollection
8285 */
8286 public function getEventsForDate($year, $month, $day){
8287 $extractEvents = null; // check if date of item is inside day given
8288 $extractEvents = $this->events->filter(function ($item) use ($year, $month, $day) {
8289 $leftDate = new \DateTime($year.'-'.$month.'-'.$day.' 00:00');
8290 $rigthDate = new \DateTime($year.'-'.$month.'-'.$day.' +1 day 00:00');
8291 return ( ( $leftDate <= $item->getDateStart() ) && ( $item->getDateStart() < $rigthDate ) );
8292
8293 }
8294 );
8295 return $extractEvents;
8296 }
8297
8298 }<?php
8299
8300 namespace Doctrine\Tests\Common\Annotations\Fixtures {
8301 use Doctrine\Tests\Common\Annotations\Fixtures\Annotation\Secure;
8302
8303 class DifferentNamespacesPerFileWithClassAsFirst {}
8304 }
8305
8306 namespace {
8307 use Doctrine\Tests\Common\Annotations\Fixtures\Annotation\Route;
8308 }
8309
8310 namespace Doctrine\Tests\Common\Annotations\Fixtures\Foo {
8311 use Doctrine\Tests\Common\Annotations\Fixtures\Annotation\Template;
8312 }<?php
8313
8314 namespace Doctrine\Tests\Common\Annotations\Fixtures\Foo {
8315 use Doctrine\Tests\Common\Annotations\Fixtures\Annotation\Secure;
8316 }
8317
8318 namespace {
8319 use Doctrine\Tests\Common\Annotations\Fixtures\Annotation\Route;
8320 }
8321
8322 namespace Doctrine\Tests\Common\Annotations\Fixtures {
8323 use Doctrine\Tests\Common\Annotations\Fixtures\Annotation\Template;
8324
8325 class DifferentNamespacesPerFileWithClassAsLast {}
8326 }<?php
8327
8328 namespace Doctrine\Tests\Common\Annotations\Fixtures;
8329
8330 /**
8331 * @Annotation
8332 * @Target({ "METHOD", "PROPERTY" })
8333 */
8334 final class AnnotationTargetPropertyMethod
8335 {
8336 public $data;
8337 public $name;
8338 public $target;
8339 }<?php
8340
8341 namespace Doctrine\Tests\Common\Annotations\Fixtures;
8342
8343 use Doctrine\Tests\Common\Annotations\Fixtures\Annotation\Secure;
8344
8345 interface TestInterface
8346 {
8347 /**
8348 * @Secure
8349 */
8350 function foo();
8351 }<?php
8352
8353 namespace Doctrine\Tests\Common\Annotations\Fixtures\Annotation;
8354
8355 /**
8356 * @Annotation
8357 * @Target("PROPERTY")
8358 */
8359 final class Version
8360 {
8361 }
8362 <?php
8363
8364 namespace Doctrine\Tests\Common\Annotations\Fixtures\Annotation;
8365
8366 /** @Annotation */
8367 class Route
8368 {
8369 /** @var string @Required */
8370 public $pattern;
8371 public $name;
8372 }<?php
8373
8374 namespace Doctrine\Tests\Common\Annotations\Fixtures\Annotation;
8375
8376 /** @Annotation */
8377 class Template
8378 {
8379 private $name;
8380
8381 public function __construct(array $values)
8382 {
8383 $this->name = isset($values['value']) ? $values['value'] : null;
8384 }
8385 }<?php
8386
8387 namespace Doctrine\Tests\Common\Annotations\Fixtures\Annotation;
8388
8389 /** @Annotation */
8390 class Secure
8391 {
8392 private $roles;
8393
8394 public function __construct(array $values)
8395 {
8396 if (is_string($values['value'])) {
8397 $values['value'] = array($values['value']);
8398 }
8399
8400 $this->roles = $values['value'];
8401 }
8402 }<?php
8403
8404 namespace Doctrine\Tests\Common\Annotations\Fixtures\Annotation;
8405
8406 /**
8407 * @Annotation
8408 */
8409 class Autoload
8410 {
8411 }<?php
8412
8413 namespace Doctrine\Tests\Common\Annotations\Fixtures\Annotation;
8414
8415 /** @Annotation */
8416 class AnnotWithDefaultValue
8417 {
8418 /** @var string */
8419 public $foo = 'bar';
8420 }<?php
8421
8422 namespace {
8423 use Doctrine\Tests\Common\Annotations\Fixtures\Annotation\Secure;
8424 use Doctrine\Tests\Common\Annotations\Fixtures\Annotation\Route;
8425
8426 class GlobalNamespacesPerFileWithClassAsFirst {}
8427 }
8428
8429 namespace {
8430 use Doctrine\Tests\Common\Annotations\Fixtures\Annotation\Template;
8431 }<?php
8432
8433 namespace Doctrine\Tests\Common\Annotations\Fixtures;
8434
8435 use Doctrine\Tests\Common\Annotations\Fixtures\Annotation\Template;
8436 use Doctrine\Tests\Common\Annotations\Fixtures\Annotation\Route;
8437
8438 /**
8439 * @Route("/someprefix")
8440 * @author Johannes M. Schmitt <schmittjoh@gmail.com>
8441 */
8442 class Controller
8443 {
8444 /**
8445 * @Route("/", name="_demo")
8446 * @Template()
8447 */
8448 public function indexAction()
8449 {
8450 return array();
8451 }
8452
8453 /**
8454 * @Route("/hello/{name}", name="_demo_hello")
8455 * @Template()
8456 */
8457 public function helloAction($name)
8458 {
8459 return array('name' => $name);
8460 }
8461
8462 /**
8463 * @Route("/contact", name="_demo_contact")
8464 * @Template()
8465 */
8466 public function contactAction()
8467 {
8468 $form = ContactForm::create($this->get('form.context'), 'contact');
8469
8470 $form->bind($this->container->get('request'), $form);
8471 if ($form->isValid()) {
8472 $form->send($this->get('mailer'));
8473
8474 $this->get('session')->setFlash('notice', 'Message sent!');
8475
8476 return new RedirectResponse($this->generateUrl('_demo'));
8477 }
8478
8479 return array('form' => $form);
8480 }
8481
8482 /**
8483 * Creates the ACL for the passed object identity
8484 *
8485 * @param ObjectIdentityInterface $oid
8486 * @return void
8487 */
8488 private function createObjectIdentity(ObjectIdentityInterface $oid)
8489 {
8490 $classId = $this->createOrRetrieveClassId($oid->getType());
8491
8492 $this->connection->executeQuery($this->getInsertObjectIdentitySql($oid->getIdentifier(), $classId, true));
8493 }
8494
8495 /**
8496 * Returns the primary key for the passed class type.
8497 *
8498 * If the type does not yet exist in the database, it will be created.
8499 *
8500 * @param string $classType
8501 * @return integer
8502 */
8503 private function createOrRetrieveClassId($classType)
8504 {
8505 if (false !== $id = $this->connection->executeQuery($this->getSelectClassIdSql($classType))->fetchColumn()) {
8506 return $id;
8507 }
8508
8509 $this->connection->executeQuery($this->getInsertClassSql($classType));
8510
8511 return $this->connection->executeQuery($this->getSelectClassIdSql($classType))->fetchColumn();
8512 }
8513
8514 /**
8515 * Returns the primary key for the passed security identity.
8516 *
8517 * If the security identity does not yet exist in the database, it will be
8518 * created.
8519 *
8520 * @param SecurityIdentityInterface $sid
8521 * @return integer
8522 */
8523 private function createOrRetrieveSecurityIdentityId(SecurityIdentityInterface $sid)
8524 {
8525 if (false !== $id = $this->connection->executeQuery($this->getSelectSecurityIdentityIdSql($sid))->fetchColumn()) {
8526 return $id;
8527 }
8528
8529 $this->connection->executeQuery($this->getInsertSecurityIdentitySql($sid));
8530
8531 return $this->connection->executeQuery($this->getSelectSecurityIdentityIdSql($sid))->fetchColumn();
8532 }
8533
8534 /**
8535 * Deletes all ACEs for the given object identity primary key.
8536 *
8537 * @param integer $oidPK
8538 * @return void
8539 */
8540 private function deleteAccessControlEntries($oidPK)
8541 {
8542 $this->connection->executeQuery($this->getDeleteAccessControlEntriesSql($oidPK));
8543 }
8544
8545 /**
8546 * Deletes the object identity from the database.
8547 *
8548 * @param integer $pk
8549 * @return void
8550 */
8551 private function deleteObjectIdentity($pk)
8552 {
8553 $this->connection->executeQuery($this->getDeleteObjectIdentitySql($pk));
8554 }
8555
8556 /**
8557 * Deletes all entries from the relations table from the database.
8558 *
8559 * @param integer $pk
8560 * @return void
8561 */
8562 private function deleteObjectIdentityRelations($pk)
8563 {
8564 $this->connection->executeQuery($this->getDeleteObjectIdentityRelationsSql($pk));
8565 }
8566
8567 /**
8568 * This regenerates the ancestor table which is used for fast read access.
8569 *
8570 * @param AclInterface $acl
8571 * @return void
8572 */
8573 private function regenerateAncestorRelations(AclInterface $acl)
8574 {
8575 $pk = $acl->getId();
8576 $this->connection->executeQuery($this->getDeleteObjectIdentityRelationsSql($pk));
8577 $this->connection->executeQuery($this->getInsertObjectIdentityRelationSql($pk, $pk));
8578
8579 $parentAcl = $acl->getParentAcl();
8580 while (null !== $parentAcl) {
8581 $this->connection->executeQuery($this->getInsertObjectIdentityRelationSql($pk, $parentAcl->getId()));
8582
8583 $parentAcl = $parentAcl->getParentAcl();
8584 }
8585 }
8586
8587 /**
8588 * This processes changes on an ACE related property (classFieldAces, or objectFieldAces).
8589 *
8590 * @param string $name
8591 * @param array $changes
8592 * @return void
8593 */
8594 private function updateFieldAceProperty($name, array $changes)
8595 {
8596 $sids = new \SplObjectStorage();
8597 $classIds = new \SplObjectStorage();
8598 $currentIds = array();
8599 foreach ($changes[1] as $field => $new) {
8600 for ($i=0,$c=count($new); $i<$c; $i++) {
8601 $ace = $new[$i];
8602
8603 if (null === $ace->getId()) {
8604 if ($sids->contains($ace->getSecurityIdentity())) {
8605 $sid = $sids->offsetGet($ace->getSecurityIdentity());
8606 } else {
8607 $sid = $this->createOrRetrieveSecurityIdentityId($ace->getSecurityIdentity());
8608 }
8609
8610 $oid = $ace->getAcl()->getObjectIdentity();
8611 if ($classIds->contains($oid)) {
8612 $classId = $classIds->offsetGet($oid);
8613 } else {
8614 $classId = $this->createOrRetrieveClassId($oid->getType());
8615 }
8616
8617 $objectIdentityId = $name === 'classFieldAces' ? null : $ace->getAcl()->getId();
8618
8619 $this->connection->executeQuery($this->getInsertAccessControlEntrySql($classId, $objectIdentityId, $field, $i, $sid, $ace->getStrategy(), $ace->getMask(), $ace->isGranting(), $ace->isAuditSuccess(), $ace->isAuditFailure()));
8620 $aceId = $this->connection->executeQuery($this->getSelectAccessControlEntryIdSql($classId, $objectIdentityId, $field, $i))->fetchColumn();
8621 $this->loadedAces[$aceId] = $ace;
8622
8623 $aceIdProperty = new \ReflectionProperty('Symfony\Component\Security\Acl\Domain\Entry', 'id');
8624 $aceIdProperty->setAccessible(true);
8625 $aceIdProperty->setValue($ace, intval($aceId));
8626 } else {
8627 $currentIds[$ace->getId()] = true;
8628 }
8629 }
8630 }
8631
8632 foreach ($changes[0] as $old) {
8633 for ($i=0,$c=count($old); $i<$c; $i++) {
8634 $ace = $old[$i];
8635
8636 if (!isset($currentIds[$ace->getId()])) {
8637 $this->connection->executeQuery($this->getDeleteAccessControlEntrySql($ace->getId()));
8638 unset($this->loadedAces[$ace->getId()]);
8639 }
8640 }
8641 }
8642 }
8643
8644 /**
8645 * This processes changes on an ACE related property (classAces, or objectAces).
8646 *
8647 * @param string $name
8648 * @param array $changes
8649 * @return void
8650 */
8651 private function updateAceProperty($name, array $changes)
8652 {
8653 list($old, $new) = $changes;
8654
8655 $sids = new \SplObjectStorage();
8656 $classIds = new \SplObjectStorage();
8657 $currentIds = array();
8658 for ($i=0,$c=count($new); $i<$c; $i++) {
8659 $ace = $new[$i];
8660
8661 if (null === $ace->getId()) {
8662 if ($sids->contains($ace->getSecurityIdentity())) {
8663 $sid = $sids->offsetGet($ace->getSecurityIdentity());
8664 } else {
8665 $sid = $this->createOrRetrieveSecurityIdentityId($ace->getSecurityIdentity());
8666 }
8667
8668 $oid = $ace->getAcl()->getObjectIdentity();
8669 if ($classIds->contains($oid)) {
8670 $classId = $classIds->offsetGet($oid);
8671 } else {
8672 $classId = $this->createOrRetrieveClassId($oid->getType());
8673 }
8674
8675 $objectIdentityId = $name === 'classAces' ? null : $ace->getAcl()->getId();
8676
8677 $this->connection->executeQuery($this->getInsertAccessControlEntrySql($classId, $objectIdentityId, null, $i, $sid, $ace->getStrategy(), $ace->getMask(), $ace->isGranting(), $ace->isAuditSuccess(), $ace->isAuditFailure()));
8678 $aceId = $this->connection->executeQuery($this->getSelectAccessControlEntryIdSql($classId, $objectIdentityId, null, $i))->fetchColumn();
8679 $this->loadedAces[$aceId] = $ace;
8680
8681 $aceIdProperty = new \ReflectionProperty($ace, 'id');
8682 $aceIdProperty->setAccessible(true);
8683 $aceIdProperty->setValue($ace, intval($aceId));
8684 } else {
8685 $currentIds[$ace->getId()] = true;
8686 }
8687 }
8688
8689 for ($i=0,$c=count($old); $i<$c; $i++) {
8690 $ace = $old[$i];
8691
8692 if (!isset($currentIds[$ace->getId()])) {
8693 $this->connection->executeQuery($this->getDeleteAccessControlEntrySql($ace->getId()));
8694 unset($this->loadedAces[$ace->getId()]);
8695 }
8696 }
8697 }
8698
8699 /**
8700 * Persists the changes which were made to ACEs to the database.
8701 *
8702 * @param \SplObjectStorage $aces
8703 * @return void
8704 */
8705 private function updateAces(\SplObjectStorage $aces)
8706 {
8707 foreach ($aces as $ace) {
8708 $propertyChanges = $aces->offsetGet($ace);
8709 $sets = array();
8710
8711 if (isset($propertyChanges['mask'])) {
8712 $sets[] = sprintf('mask = %d', $propertyChanges['mask'][1]);
8713 }
8714 if (isset($propertyChanges['strategy'])) {
8715 $sets[] = sprintf('granting_strategy = %s', $this->connection->quote($propertyChanges['strategy']));
8716 }
8717 if (isset($propertyChanges['aceOrder'])) {
8718 $sets[] = sprintf('ace_order = %d', $propertyChanges['aceOrder'][1]);
8719 }
8720 if (isset($propertyChanges['auditSuccess'])) {
8721 $sets[] = sprintf('audit_success = %s', $this->connection->getDatabasePlatform()->convertBooleans($propertyChanges['auditSuccess'][1]));
8722 }
8723 if (isset($propertyChanges['auditFailure'])) {
8724 $sets[] = sprintf('audit_failure = %s', $this->connection->getDatabasePlatform()->convertBooleans($propertyChanges['auditFailure'][1]));
8725 }
8726
8727 $this->connection->executeQuery($this->getUpdateAccessControlEntrySql($ace->getId(), $sets));
8728 }
8729 }
8730 }<?php
8731
8732 namespace Doctrine\Tests\Common\Annotations\Fixtures;
8733
8734 use ReflectionClass, ReflectionProperty, ReflectionMethod;
8735 use Doctrine\Tests\Common\Annotations\Fixtures\Annotation\Secure;
8736 use Doctrine\Tests\Common\Annotations\Fixtures\Annotation\Route;
8737
8738 class NamespacedSingleClassLOC1000
8739 {
8740 const TEST1 = 1234567890;
8741 const TEST2 = 1234567890;
8742 const TEST3 = 1234567890;
8743 const TEST4 = 1234567890;
8744 const TEST5 = 1234567890;
8745 const TEST6 = 1234567890;
8746 const TEST7 = 1234567890;
8747 const TEST8 = 1234567890;
8748 const TEST9 = 1234567890;
8749
8750 private $test1 = null;
8751 private $test2 = null;
8752 private $test3 = null;
8753 private $test4 = null;
8754 private $test5 = null;
8755 private $test6 = null;
8756 private $test7 = null;
8757 private $test8 = null;
8758 private $test9 = null;
8759
8760 public function test1()
8761 {
8762 echo $this->test1;
8763 echo $this->test2;
8764 echo $this->test3;
8765 $array = array(1, 2, 3, 4, 5, 6, 7, 8, 9, 10);
8766 foreach ($array as $key => $value) {
8767 echo $key . ' => ' . $value;
8768 }
8769
8770 $val = (string)self::TEST1;
8771 $val .= (string)self::TEST2;
8772 $val .= (string)self::TEST3;
8773 $val .= (string)self::TEST4;
8774 $val .= (string)self::TEST5;
8775 $val .= (string)self::TEST6;
8776 $val .= (string)self::TEST7;
8777 $val .= (string)self::TEST8;
8778 $val .= (string)self::TEST9;
8779
8780 strtolower($val);
8781
8782 return $val;
8783 }
8784
8785 public function test2()
8786 {
8787 echo $this->test1;
8788 echo $this->test2;
8789 echo $this->test3;
8790 $array = array(1, 2, 3, 4, 5, 6, 7, 8, 9, 10);
8791 foreach ($array as $key => $value) {
8792 echo $key . ' => ' . $value;
8793 }
8794
8795 $val = (string)self::TEST1;
8796 $val .= (string)self::TEST2;
8797 $val .= (string)self::TEST3;
8798 $val .= (string)self::TEST4;
8799 $val .= (string)self::TEST5;
8800 $val .= (string)self::TEST6;
8801 $val .= (string)self::TEST7;
8802 $val .= (string)self::TEST8;
8803 $val .= (string)self::TEST9;
8804
8805 strtolower($val);
8806
8807 return $val;
8808 }
8809
8810 public function test3()
8811 {
8812 echo $this->test1;
8813 echo $this->test2;
8814 echo $this->test3;
8815 $array = array(1, 2, 3, 4, 5, 6, 7, 8, 9, 10);
8816 foreach ($array as $key => $value) {
8817 echo $key . ' => ' . $value;
8818 }
8819
8820 $val = (string)self::TEST1;
8821 $val .= (string)self::TEST2;
8822 $val .= (string)self::TEST3;
8823 $val .= (string)self::TEST4;
8824 $val .= (string)self::TEST5;
8825 $val .= (string)self::TEST6;
8826 $val .= (string)self::TEST7;
8827 $val .= (string)self::TEST8;
8828 $val .= (string)self::TEST9;
8829
8830 strtolower($val);
8831
8832 return $val;
8833 }
8834
8835 public function test4()
8836 {
8837 echo $this->test1;
8838 echo $this->test2;
8839 echo $this->test3;
8840 $array = array(1, 2, 3, 4, 5, 6, 7, 8, 9, 10);
8841 foreach ($array as $key => $value) {
8842 echo $key . ' => ' . $value;
8843 }
8844
8845 $val = (string)self::TEST1;
8846 $val .= (string)self::TEST2;
8847 $val .= (string)self::TEST3;
8848 $val .= (string)self::TEST4;
8849 $val .= (string)self::TEST5;
8850 $val .= (string)self::TEST6;
8851 $val .= (string)self::TEST7;
8852 $val .= (string)self::TEST8;
8853 $val .= (string)self::TEST9;
8854
8855 strtolower($val);
8856
8857 return $val;
8858 }
8859
8860 public function test5()
8861 {
8862 echo $this->test1;
8863 echo $this->test2;
8864 echo $this->test3;
8865 $array = array(1, 2, 3, 4, 5, 6, 7, 8, 9, 10);
8866 foreach ($array as $key => $value) {
8867 echo $key . ' => ' . $value;
8868 }
8869
8870 $val = (string)self::TEST1;
8871 $val .= (string)self::TEST2;
8872 $val .= (string)self::TEST3;
8873 $val .= (string)self::TEST4;
8874 $val .= (string)self::TEST5;
8875 $val .= (string)self::TEST6;
8876 $val .= (string)self::TEST7;
8877 $val .= (string)self::TEST8;
8878 $val .= (string)self::TEST9;
8879
8880 strtolower($val);
8881
8882 return $val;
8883 }
8884
8885 public function test6()
8886 {
8887 echo $this->test1;
8888 echo $this->test2;
8889 echo $this->test3;
8890 $array = array(1, 2, 3, 4, 5, 6, 7, 8, 9, 10);
8891 foreach ($array as $key => $value) {
8892 echo $key . ' => ' . $value;
8893 }
8894
8895 $val = (string)self::TEST1;
8896 $val .= (string)self::TEST2;
8897 $val .= (string)self::TEST3;
8898 $val .= (string)self::TEST4;
8899 $val .= (string)self::TEST5;
8900 $val .= (string)self::TEST6;
8901 $val .= (string)self::TEST7;
8902 $val .= (string)self::TEST8;
8903 $val .= (string)self::TEST9;
8904
8905 strtolower($val);
8906
8907 return $val;
8908 }
8909
8910 public function test7()
8911 {
8912 echo $this->test1;
8913 echo $this->test2;
8914 echo $this->test3;
8915 $array = array(1, 2, 3, 4, 5, 6, 7, 8, 9, 10);
8916 foreach ($array as $key => $value) {
8917 echo $key . ' => ' . $value;
8918 }
8919
8920 $val = (string)self::TEST1;
8921 $val .= (string)self::TEST2;
8922 $val .= (string)self::TEST3;
8923 $val .= (string)self::TEST4;
8924 $val .= (string)self::TEST5;
8925 $val .= (string)self::TEST6;
8926 $val .= (string)self::TEST7;
8927 $val .= (string)self::TEST8;
8928 $val .= (string)self::TEST9;
8929
8930 strtolower($val);
8931
8932 return $val;
8933 }
8934
8935 public function test8()
8936 {
8937 echo $this->test1;
8938 echo $this->test2;
8939 echo $this->test3;
8940 $array = array(1, 2, 3, 4, 5, 6, 7, 8, 9, 10);
8941 foreach ($array as $key => $value) {
8942 echo $key . ' => ' . $value;
8943 }
8944
8945 $val = (string)self::TEST1;
8946 $val .= (string)self::TEST2;
8947 $val .= (string)self::TEST3;
8948 $val .= (string)self::TEST4;
8949 $val .= (string)self::TEST5;
8950 $val .= (string)self::TEST6;
8951 $val .= (string)self::TEST7;
8952 $val .= (string)self::TEST8;
8953 $val .= (string)self::TEST9;
8954
8955 strtolower($val);
8956
8957 return $val;
8958
8959 }
8960
8961 public function test9()
8962 {
8963 echo $this->test1;
8964 echo $this->test2;
8965 echo $this->test3;
8966 $array = array(1, 2, 3, 4, 5, 6, 7, 8, 9, 10);
8967 foreach ($array as $key => $value) {
8968 echo $key . ' => ' . $value;
8969 }
8970
8971 $val = (string)self::TEST1;
8972 $val .= (string)self::TEST2;
8973 $val .= (string)self::TEST3;
8974 $val .= (string)self::TEST4;
8975 $val .= (string)self::TEST5;
8976 $val .= (string)self::TEST6;
8977 $val .= (string)self::TEST7;
8978 $val .= (string)self::TEST8;
8979 $val .= (string)self::TEST9;
8980
8981 strtolower($val);
8982
8983 return $val;
8984 }
8985
8986 public function test10()
8987 {
8988 echo $this->test1;
8989 echo $this->test2;
8990 echo $this->test3;
8991 $array = array(1, 2, 3, 4, 5, 6, 7, 8, 9, 10);
8992 foreach ($array as $key => $value) {
8993 echo $key . ' => ' . $value;
8994 }
8995
8996 $val = (string)self::TEST1;
8997 $val .= (string)self::TEST2;
8998 $val .= (string)self::TEST3;
8999 $val .= (string)self::TEST4;
9000 $val .= (string)self::TEST5;
9001 $val .= (string)self::TEST6;
9002 $val .= (string)self::TEST7;
9003 $val .= (string)self::TEST8;
9004 $val .= (string)self::TEST9;
9005
9006 strtolower($val);
9007
9008 return $val;
9009 }
9010
9011 public function test11()
9012 {
9013 echo $this->test1;
9014 echo $this->test2;
9015 echo $this->test3;
9016 $array = array(1, 2, 3, 4, 5, 6, 7, 8, 9, 10);
9017 foreach ($array as $key => $value) {
9018 echo $key . ' => ' . $value;
9019 }
9020
9021 $val = (string)self::TEST1;
9022 $val .= (string)self::TEST2;
9023 $val .= (string)self::TEST3;
9024 $val .= (string)self::TEST4;
9025 $val .= (string)self::TEST5;
9026 $val .= (string)self::TEST6;
9027 $val .= (string)self::TEST7;
9028 $val .= (string)self::TEST8;
9029 $val .= (string)self::TEST9;
9030
9031 strtolower($val);
9032
9033 return $val;
9034 }
9035
9036 public function test12()
9037 {
9038 echo $this->test1;
9039 echo $this->test2;
9040 echo $this->test3;
9041 $array = array(1, 2, 3, 4, 5, 6, 7, 8, 9, 10);
9042 foreach ($array as $key => $value) {
9043 echo $key . ' => ' . $value;
9044 }
9045
9046 $val = (string)self::TEST1;
9047 $val .= (string)self::TEST2;
9048 $val .= (string)self::TEST3;
9049 $val .= (string)self::TEST4;
9050 $val .= (string)self::TEST5;
9051 $val .= (string)self::TEST6;
9052 $val .= (string)self::TEST7;
9053 $val .= (string)self::TEST8;
9054 $val .= (string)self::TEST9;
9055
9056 strtolower($val);
9057
9058 return $val;
9059 }
9060
9061 public function test13()
9062 {
9063 echo $this->test1;
9064 echo $this->test2;
9065 echo $this->test3;
9066 $array = array(1, 2, 3, 4, 5, 6, 7, 8, 9, 10);
9067 foreach ($array as $key => $value) {
9068 echo $key . ' => ' . $value;
9069 }
9070
9071 $val = (string)self::TEST1;
9072 $val .= (string)self::TEST2;
9073 $val .= (string)self::TEST3;
9074 $val .= (string)self::TEST4;
9075 $val .= (string)self::TEST5;
9076 $val .= (string)self::TEST6;
9077 $val .= (string)self::TEST7;
9078 $val .= (string)self::TEST8;
9079 $val .= (string)self::TEST9;
9080
9081 strtolower($val);
9082
9083 return $val;
9084 }
9085
9086 public function test14()
9087 {
9088 echo $this->test1;
9089 echo $this->test2;
9090 echo $this->test3;
9091 $array = array(1, 2, 3, 4, 5, 6, 7, 8, 9, 10);
9092 foreach ($array as $key => $value) {
9093 echo $key . ' => ' . $value;
9094 }
9095
9096 $val = (string)self::TEST1;
9097 $val .= (string)self::TEST2;
9098 $val .= (string)self::TEST3;
9099 $val .= (string)self::TEST4;
9100 $val .= (string)self::TEST5;
9101 $val .= (string)self::TEST6;
9102 $val .= (string)self::TEST7;
9103 $val .= (string)self::TEST8;
9104 $val .= (string)self::TEST9;
9105
9106 strtolower($val);
9107
9108 return $val;
9109 }
9110
9111 public function test15()
9112 {
9113 echo $this->test1;
9114 echo $this->test2;
9115 echo $this->test3;
9116 $array = array(1, 2, 3, 4, 5, 6, 7, 8, 9, 10);
9117 foreach ($array as $key => $value) {
9118 echo $key . ' => ' . $value;
9119 }
9120
9121 $val = (string)self::TEST1;
9122 $val .= (string)self::TEST2;
9123 $val .= (string)self::TEST3;
9124 $val .= (string)self::TEST4;
9125 $val .= (string)self::TEST5;
9126 $val .= (string)self::TEST6;
9127 $val .= (string)self::TEST7;
9128 $val .= (string)self::TEST8;
9129 $val .= (string)self::TEST9;
9130
9131 strtolower($val);
9132
9133 return $val;
9134 }
9135
9136 public function test16()
9137 {
9138 echo $this->test1;
9139 echo $this->test2;
9140 echo $this->test3;
9141 $array = array(1, 2, 3, 4, 5, 6, 7, 8, 9, 10);
9142 foreach ($array as $key => $value) {
9143 echo $key . ' => ' . $value;
9144 }
9145
9146 $val = (string)self::TEST1;
9147 $val .= (string)self::TEST2;
9148 $val .= (string)self::TEST3;
9149 $val .= (string)self::TEST4;
9150 $val .= (string)self::TEST5;
9151 $val .= (string)self::TEST6;
9152 $val .= (string)self::TEST7;
9153 $val .= (string)self::TEST8;
9154 $val .= (string)self::TEST9;
9155
9156 strtolower($val);
9157
9158 return $val;
9159 }
9160
9161 public function test17()
9162 {
9163 echo $this->test1;
9164 echo $this->test2;
9165 echo $this->test3;
9166 $array = array(1, 2, 3, 4, 5, 6, 7, 8, 9, 10);
9167 foreach ($array as $key => $value) {
9168 echo $key . ' => ' . $value;
9169 }
9170
9171 $val = (string)self::TEST1;
9172 $val .= (string)self::TEST2;
9173 $val .= (string)self::TEST3;
9174 $val .= (string)self::TEST4;
9175 $val .= (string)self::TEST5;
9176 $val .= (string)self::TEST6;
9177 $val .= (string)self::TEST7;
9178 $val .= (string)self::TEST8;
9179 $val .= (string)self::TEST9;
9180
9181 strtolower($val);
9182
9183 return $val;
9184
9185 }
9186
9187 public function test18()
9188 {
9189 echo $this->test1;
9190 echo $this->test2;
9191 echo $this->test3;
9192 $array = array(1, 2, 3, 4, 5, 6, 7, 8, 9, 10);
9193 foreach ($array as $key => $value) {
9194 echo $key . ' => ' . $value;
9195 }
9196
9197 $val = (string)self::TEST1;
9198 $val .= (string)self::TEST2;
9199 $val .= (string)self::TEST3;
9200 $val .= (string)self::TEST4;
9201 $val .= (string)self::TEST5;
9202 $val .= (string)self::TEST6;
9203 $val .= (string)self::TEST7;
9204 $val .= (string)self::TEST8;
9205 $val .= (string)self::TEST9;
9206
9207 strtolower($val);
9208
9209 return $val;
9210 }
9211
9212 public function test19()
9213 {
9214 echo $this->test1;
9215 echo $this->test2;
9216 echo $this->test3;
9217 $array = array(1, 2, 3, 4, 5, 6, 7, 8, 9, 10);
9218 foreach ($array as $key => $value) {
9219 echo $key . ' => ' . $value;
9220 }
9221
9222 $val = (string)self::TEST1;
9223 $val .= (string)self::TEST2;
9224 $val .= (string)self::TEST3;
9225 $val .= (string)self::TEST4;
9226 $val .= (string)self::TEST5;
9227 $val .= (string)self::TEST6;
9228 $val .= (string)self::TEST7;
9229 $val .= (string)self::TEST8;
9230 $val .= (string)self::TEST9;
9231
9232 strtolower($val);
9233
9234 return $val;
9235 }
9236
9237 public function test20()
9238 {
9239 echo $this->test1;
9240 echo $this->test2;
9241 echo $this->test3;
9242 $array = array(1, 2, 3, 4, 5, 6, 7, 8, 9, 10);
9243 foreach ($array as $key => $value) {
9244 echo $key . ' => ' . $value;
9245 }
9246
9247 $val = (string)self::TEST1;
9248 $val .= (string)self::TEST2;
9249 $val .= (string)self::TEST3;
9250 $val .= (string)self::TEST4;
9251 $val .= (string)self::TEST5;
9252 $val .= (string)self::TEST6;
9253 $val .= (string)self::TEST7;
9254 $val .= (string)self::TEST8;
9255 $val .= (string)self::TEST9;
9256
9257 strtolower($val);
9258
9259 return $val;
9260 }
9261
9262 public function test21()
9263 {
9264 echo $this->test1;
9265 echo $this->test2;
9266 echo $this->test3;
9267 $array = array(1, 2, 3, 4, 5, 6, 7, 8, 9, 10);
9268 foreach ($array as $key => $value) {
9269 echo $key . ' => ' . $value;
9270 }
9271
9272 $val = (string)self::TEST1;
9273 $val .= (string)self::TEST2;
9274 $val .= (string)self::TEST3;
9275 $val .= (string)self::TEST4;
9276 $val .= (string)self::TEST5;
9277 $val .= (string)self::TEST6;
9278 $val .= (string)self::TEST7;
9279 $val .= (string)self::TEST8;
9280 $val .= (string)self::TEST9;
9281
9282 strtolower($val);
9283
9284 return $val;
9285 }
9286
9287 public function test22()
9288 {
9289 echo $this->test1;
9290 echo $this->test2;
9291 echo $this->test3;
9292 $array = array(1, 2, 3, 4, 5, 6, 7, 8, 9, 10);
9293 foreach ($array as $key => $value) {
9294 echo $key . ' => ' . $value;
9295 }
9296
9297 $val = (string)self::TEST1;
9298 $val .= (string)self::TEST2;
9299 $val .= (string)self::TEST3;
9300 $val .= (string)self::TEST4;
9301 $val .= (string)self::TEST5;
9302 $val .= (string)self::TEST6;
9303 $val .= (string)self::TEST7;
9304 $val .= (string)self::TEST8;
9305 $val .= (string)self::TEST9;
9306
9307 strtolower($val);
9308
9309 return $val;
9310 }
9311
9312 public function test23()
9313 {
9314 echo $this->test1;
9315 echo $this->test2;
9316 echo $this->test3;
9317 $array = array(1, 2, 3, 4, 5, 6, 7, 8, 9, 10);
9318 foreach ($array as $key => $value) {
9319 echo $key . ' => ' . $value;
9320 }
9321
9322 $val = (string)self::TEST1;
9323 $val .= (string)self::TEST2;
9324 $val .= (string)self::TEST3;
9325 $val .= (string)self::TEST4;
9326 $val .= (string)self::TEST5;
9327 $val .= (string)self::TEST6;
9328 $val .= (string)self::TEST7;
9329 $val .= (string)self::TEST8;
9330 $val .= (string)self::TEST9;
9331
9332 strtolower($val);
9333
9334 return $val;
9335 }
9336
9337 public function test24()
9338 {
9339 echo $this->test1;
9340 echo $this->test2;
9341 echo $this->test3;
9342 $array = array(1, 2, 3, 4, 5, 6, 7, 8, 9, 10);
9343 foreach ($array as $key => $value) {
9344 echo $key . ' => ' . $value;
9345 }
9346
9347 $val = (string)self::TEST1;
9348 $val .= (string)self::TEST2;
9349 $val .= (string)self::TEST3;
9350 $val .= (string)self::TEST4;
9351 $val .= (string)self::TEST5;
9352 $val .= (string)self::TEST6;
9353 $val .= (string)self::TEST7;
9354 $val .= (string)self::TEST8;
9355 $val .= (string)self::TEST9;
9356
9357 strtolower($val);
9358
9359 return $val;
9360 }
9361
9362 public function test25()
9363 {
9364 echo $this->test1;
9365 echo $this->test2;
9366 echo $this->test3;
9367 $array = array(1, 2, 3, 4, 5, 6, 7, 8, 9, 10);
9368 foreach ($array as $key => $value) {
9369 echo $key . ' => ' . $value;
9370 }
9371
9372 $val = (string)self::TEST1;
9373 $val .= (string)self::TEST2;
9374 $val .= (string)self::TEST3;
9375 $val .= (string)self::TEST4;
9376 $val .= (string)self::TEST5;
9377 $val .= (string)self::TEST6;
9378 $val .= (string)self::TEST7;
9379 $val .= (string)self::TEST8;
9380 $val .= (string)self::TEST9;
9381
9382 strtolower($val);
9383
9384 return $val;
9385 }
9386
9387 public function test26()
9388 {
9389 echo $this->test1;
9390 echo $this->test2;
9391 echo $this->test3;
9392 $array = array(1, 2, 3, 4, 5, 6, 7, 8, 9, 10);
9393 foreach ($array as $key => $value) {
9394 echo $key . ' => ' . $value;
9395 }
9396
9397 $val = (string)self::TEST1;
9398 $val .= (string)self::TEST2;
9399 $val .= (string)self::TEST3;
9400 $val .= (string)self::TEST4;
9401 $val .= (string)self::TEST5;
9402 $val .= (string)self::TEST6;
9403 $val .= (string)self::TEST7;
9404 $val .= (string)self::TEST8;
9405 $val .= (string)self::TEST9;
9406
9407 strtolower($val);
9408
9409 return $val;
9410 }
9411
9412 public function test27()
9413 {
9414 echo $this->test1;
9415 echo $this->test2;
9416 echo $this->test3;
9417 $array = array(1, 2, 3, 4, 5, 6, 7, 8, 9, 10);
9418 foreach ($array as $key => $value) {
9419 echo $key . ' => ' . $value;
9420 }
9421
9422 $val = (string)self::TEST1;
9423 $val .= (string)self::TEST2;
9424 $val .= (string)self::TEST3;
9425 $val .= (string)self::TEST4;
9426 $val .= (string)self::TEST5;
9427 $val .= (string)self::TEST6;
9428 $val .= (string)self::TEST7;
9429 $val .= (string)self::TEST8;
9430 $val .= (string)self::TEST9;
9431
9432 strtolower($val);
9433
9434 return $val;
9435
9436 }
9437
9438 public function test28()
9439 {
9440 echo $this->test1;
9441 echo $this->test2;
9442 echo $this->test3;
9443 $array = array(1, 2, 3, 4, 5, 6, 7, 8, 9, 10);
9444 foreach ($array as $key => $value) {
9445 echo $key . ' => ' . $value;
9446 }
9447
9448 $val = (string)self::TEST1;
9449 $val .= (string)self::TEST2;
9450 $val .= (string)self::TEST3;
9451 $val .= (string)self::TEST4;
9452 $val .= (string)self::TEST5;
9453 $val .= (string)self::TEST6;
9454 $val .= (string)self::TEST7;
9455 $val .= (string)self::TEST8;
9456 $val .= (string)self::TEST9;
9457
9458 strtolower($val);
9459
9460 return $val;
9461 }
9462
9463 public function test29()
9464 {
9465 echo $this->test1;
9466 echo $this->test2;
9467 echo $this->test3;
9468 $array = array(1, 2, 3, 4, 5, 6, 7, 8, 9, 10);
9469 foreach ($array as $key => $value) {
9470 echo $key . ' => ' . $value;
9471 }
9472
9473 $val = (string)self::TEST1;
9474 $val .= (string)self::TEST2;
9475 $val .= (string)self::TEST3;
9476 $val .= (string)self::TEST4;
9477 $val .= (string)self::TEST5;
9478 $val .= (string)self::TEST6;
9479 $val .= (string)self::TEST7;
9480 $val .= (string)self::TEST8;
9481 $val .= (string)self::TEST9;
9482
9483 strtolower($val);
9484
9485 return $val;
9486 }
9487
9488 public function test30()
9489 {
9490 echo $this->test1;
9491 echo $this->test2;
9492 echo $this->test3;
9493 $array = array(1, 2, 3, 4, 5, 6, 7, 8, 9, 10);
9494 foreach ($array as $key => $value) {
9495 echo $key . ' => ' . $value;
9496 }
9497
9498 $val = (string)self::TEST1;
9499 $val .= (string)self::TEST2;
9500 $val .= (string)self::TEST3;
9501 $val .= (string)self::TEST4;
9502 $val .= (string)self::TEST5;
9503 $val .= (string)self::TEST6;
9504 $val .= (string)self::TEST7;
9505 $val .= (string)self::TEST8;
9506 $val .= (string)self::TEST9;
9507
9508 strtolower($val);
9509
9510 return $val;
9511 }
9512
9513 public function test31()
9514 {
9515 echo $this->test1;
9516 echo $this->test2;
9517 echo $this->test3;
9518 $array = array(1, 2, 3, 4, 5, 6, 7, 8, 9, 10);
9519 foreach ($array as $key => $value) {
9520 echo $key . ' => ' . $value;
9521 }
9522
9523 $val = (string)self::TEST1;
9524 $val .= (string)self::TEST2;
9525 $val .= (string)self::TEST3;
9526 $val .= (string)self::TEST4;
9527 $val .= (string)self::TEST5;
9528 $val .= (string)self::TEST6;
9529 $val .= (string)self::TEST7;
9530 $val .= (string)self::TEST8;
9531 $val .= (string)self::TEST9;
9532
9533 strtolower($val);
9534
9535 return $val;
9536 }
9537
9538 public function test32()
9539 {
9540 echo $this->test1;
9541 echo $this->test2;
9542 echo $this->test3;
9543 $array = array(1, 2, 3, 4, 5, 6, 7, 8, 9, 10);
9544 foreach ($array as $key => $value) {
9545 echo $key . ' => ' . $value;
9546 }
9547
9548 $val = (string)self::TEST1;
9549 $val .= (string)self::TEST2;
9550 $val .= (string)self::TEST3;
9551 $val .= (string)self::TEST4;
9552 $val .= (string)self::TEST5;
9553 $val .= (string)self::TEST6;
9554 $val .= (string)self::TEST7;
9555 $val .= (string)self::TEST8;
9556 $val .= (string)self::TEST9;
9557
9558 strtolower($val);
9559
9560 return $val;
9561 }
9562
9563 public function test33()
9564 {
9565 echo $this->test1;
9566 echo $this->test2;
9567 echo $this->test3;
9568 $array = array(1, 2, 3, 4, 5, 6, 7, 8, 9, 10);
9569 foreach ($array as $key => $value) {
9570 echo $key . ' => ' . $value;
9571 }
9572
9573 $val = (string)self::TEST1;
9574 $val .= (string)self::TEST2;
9575 $val .= (string)self::TEST3;
9576 $val .= (string)self::TEST4;
9577 $val .= (string)self::TEST5;
9578 $val .= (string)self::TEST6;
9579 $val .= (string)self::TEST7;
9580 $val .= (string)self::TEST8;
9581 $val .= (string)self::TEST9;
9582
9583 strtolower($val);
9584
9585 return $val;
9586 }
9587
9588 public function test34()
9589 {
9590 echo $this->test1;
9591 echo $this->test2;
9592 echo $this->test3;
9593 $array = array(1, 2, 3, 4, 5, 6, 7, 8, 9, 10);
9594 foreach ($array as $key => $value) {
9595 echo $key . ' => ' . $value;
9596 }
9597
9598 $val = (string)self::TEST1;
9599 $val .= (string)self::TEST2;
9600 $val .= (string)self::TEST3;
9601 $val .= (string)self::TEST4;
9602 $val .= (string)self::TEST5;
9603 $val .= (string)self::TEST6;
9604 $val .= (string)self::TEST7;
9605 $val .= (string)self::TEST8;
9606 $val .= (string)self::TEST9;
9607
9608 strtolower($val);
9609
9610 return $val;
9611 }
9612
9613 public function test35()
9614 {
9615 echo $this->test1;
9616 echo $this->test2;
9617 echo $this->test3;
9618 $array = array(1, 2, 3, 4, 5, 6, 7, 8, 9, 10);
9619 foreach ($array as $key => $value) {
9620 echo $key . ' => ' . $value;
9621 }
9622
9623 $val = (string)self::TEST1;
9624 $val .= (string)self::TEST2;
9625 $val .= (string)self::TEST3;
9626 $val .= (string)self::TEST4;
9627 $val .= (string)self::TEST5;
9628 $val .= (string)self::TEST6;
9629 $val .= (string)self::TEST7;
9630 $val .= (string)self::TEST8;
9631 $val .= (string)self::TEST9;
9632
9633 strtolower($val);
9634
9635 return $val;
9636 }
9637
9638 public function test36()
9639 {
9640 echo $this->test1;
9641 echo $this->test2;
9642 echo $this->test3;
9643 $array = array(1, 2, 3, 4, 5, 6, 7, 8, 9, 10);
9644 foreach ($array as $key => $value) {
9645 echo $key . ' => ' . $value;
9646 }
9647
9648 $val = (string)self::TEST1;
9649 $val .= (string)self::TEST2;
9650 $val .= (string)self::TEST3;
9651 $val .= (string)self::TEST4;
9652 $val .= (string)self::TEST5;
9653 $val .= (string)self::TEST6;
9654 $val .= (string)self::TEST7;
9655 $val .= (string)self::TEST8;
9656 $val .= (string)self::TEST9;
9657
9658 strtolower($val);
9659
9660 return $val;
9661 }
9662
9663 public function test37()
9664 {
9665 echo $this->test1;
9666 echo $this->test2;
9667 echo $this->test3;
9668 $array = array(1, 2, 3, 4, 5, 6, 7, 8, 9, 10);
9669 foreach ($array as $key => $value) {
9670 echo $key . ' => ' . $value;
9671 }
9672
9673 $val = (string)self::TEST1;
9674 $val .= (string)self::TEST2;
9675 $val .= (string)self::TEST3;
9676 $val .= (string)self::TEST4;
9677 $val .= (string)self::TEST5;
9678 $val .= (string)self::TEST6;
9679 $val .= (string)self::TEST7;
9680 $val .= (string)self::TEST8;
9681 $val .= (string)self::TEST9;
9682
9683 strtolower($val);
9684
9685 return $val;
9686
9687 }
9688
9689 public function test38()
9690 {
9691 echo $this->test1;
9692 echo $this->test2;
9693 echo $this->test3;
9694 $array = array(1, 2, 3, 4, 5, 6, 7, 8, 9, 10);
9695 foreach ($array as $key => $value) {
9696 echo $key . ' => ' . $value;
9697 }
9698
9699 $val = (string)self::TEST1;
9700 $val .= (string)self::TEST2;
9701 $val .= (string)self::TEST3;
9702 $val .= (string)self::TEST4;
9703 $val .= (string)self::TEST5;
9704 $val .= (string)self::TEST6;
9705 $val .= (string)self::TEST7;
9706 $val .= (string)self::TEST8;
9707 $val .= (string)self::TEST9;
9708
9709 strtolower($val);
9710
9711 return $val;
9712 }
9713
9714 public function test39()
9715 {
9716 echo $this->test1;
9717 echo $this->test2;
9718 echo $this->test3;
9719 $array = array(1, 2, 3, 4, 5, 6, 7, 8, 9, 10);
9720 foreach ($array as $key => $value) {
9721 echo $key . ' => ' . $value;
9722 }
9723
9724 $val = (string)self::TEST1;
9725 $val .= (string)self::TEST2;
9726 $val .= (string)self::TEST3;
9727 $val .= (string)self::TEST4;
9728 $val .= (string)self::TEST5;
9729 $val .= (string)self::TEST6;
9730 $val .= (string)self::TEST7;
9731 $val .= (string)self::TEST8;
9732 $val .= (string)self::TEST9;
9733
9734 strtolower($val);
9735
9736 return $val;
9737 }
9738 }<?php
9739
9740 namespace Doctrine\Tests\Common\Annotations\Fixtures;
9741
9742 /**
9743 * @Annotation
9744 * @Target("ALL")
9745 */
9746 final class AnnotationEnumInvalid
9747 {
9748 /**
9749 * @var mixed
9750 *
9751 * @Enum({1, 2, "foo", "bar", {"foo":"bar"}})
9752 */
9753 public $value;
9754 }<?php
9755
9756 namespace Doctrine\Tests\Common\Annotations;
9757
9758 use ReflectionClass;
9759 use Doctrine\Common\Annotations\PhpParser;
9760
9761 require_once __DIR__.'/Fixtures/NonNamespacedClass.php';
9762 require_once __DIR__.'/Fixtures/GlobalNamespacesPerFileWithClassAsFirst.php';
9763 require_once __DIR__.'/Fixtures/GlobalNamespacesPerFileWithClassAsLast.php';
9764
9765 class PhpParserTest extends \PHPUnit_Framework_TestCase
9766 {
9767 public function testParseClassWithMultipleClassesInFile()
9768 {
9769 $class = new ReflectionClass(__NAMESPACE__ . '\Fixtures\MultipleClassesInFile');
9770 $parser = new PhpParser();
9771
9772 $this->assertEquals(array(
9773 'route' => __NAMESPACE__ . '\Fixtures\Annotation\Route',
9774 'secure' => __NAMESPACE__ . '\Fixtures\Annotation\Secure',
9775 ), $parser->parseClass($class));
9776 }
9777
9778 public function testParseClassWithMultipleImportsInUseStatement()
9779 {
9780 $class = new ReflectionClass(__NAMESPACE__ . '\Fixtures\MultipleImportsInUseStatement');
9781 $parser = new PhpParser();
9782
9783 $this->assertEquals(array(
9784 'route' => __NAMESPACE__ . '\Fixtures\Annotation\Route',
9785 'secure' => __NAMESPACE__ . '\Fixtures\Annotation\Secure',
9786 ), $parser->parseClass($class));
9787 }
9788
9789 public function testParseClassWhenNotUserDefined()
9790 {
9791 $parser = new PhpParser();
9792 $this->assertEquals(array(), $parser->parseClass(new \ReflectionClass('\stdClass')));
9793 }
9794
9795 public function testClassFileDoesNotExist()
9796 {
9797 $class = $this->getMockBuilder('\ReflectionClass')
9798 ->disableOriginalConstructor()
9799 ->getMock();
9800 $class->expects($this->once())
9801 ->method('getFilename')
9802 ->will($this->returnValue('/valid/class/Fake.php(35) : eval()d code'));
9803
9804 $parser = new PhpParser();
9805 $this->assertEquals(array(), $parser->parseClass($class));
9806 }
9807
9808 public function testParseClassWhenClassIsNotNamespaced()
9809 {
9810 $parser = new PhpParser();
9811 $class = new ReflectionClass('\AnnotationsTestsFixturesNonNamespacedClass');
9812
9813 $this->assertEquals(array(
9814 'route' => __NAMESPACE__ . '\Fixtures\Annotation\Route',
9815 'template' => __NAMESPACE__ . '\Fixtures\Annotation\Template',
9816 ), $parser->parseClass($class));
9817 }
9818
9819 public function testParseClassWhenClassIsInterface()
9820 {
9821 $parser = new PhpParser();
9822 $class = new ReflectionClass(__NAMESPACE__ . '\Fixtures\TestInterface');
9823
9824 $this->assertEquals(array(
9825 'secure' => __NAMESPACE__ . '\Fixtures\Annotation\Secure',
9826 ), $parser->parseClass($class));
9827 }
9828
9829 public function testClassWithFullyQualifiedUseStatements()
9830 {
9831 $parser = new PhpParser();
9832 $class = new ReflectionClass(__NAMESPACE__ . '\Fixtures\ClassWithFullyQualifiedUseStatements');
9833
9834 $this->assertEquals(array(
9835 'secure' => '\\' . __NAMESPACE__ . '\Fixtures\Annotation\Secure',
9836 'route' => '\\' . __NAMESPACE__ . '\Fixtures\Annotation\Route',
9837 'template' => '\\' . __NAMESPACE__ . '\Fixtures\Annotation\Template',
9838 ), $parser->parseClass($class));
9839 }
9840
9841 public function testNamespaceAndClassCommentedOut()
9842 {
9843 $parser = new PhpParser();
9844 $class = new ReflectionClass(__NAMESPACE__ . '\Fixtures\NamespaceAndClassCommentedOut');
9845
9846 $this->assertEquals(array(
9847 'route' => __NAMESPACE__ . '\Fixtures\Annotation\Route',
9848 'template' => __NAMESPACE__ . '\Fixtures\Annotation\Template',
9849 ), $parser->parseClass($class));
9850 }
9851
9852 public function testEqualNamespacesPerFileWithClassAsFirst()
9853 {
9854 $parser = new PhpParser();
9855 $class = new ReflectionClass(__NAMESPACE__ . '\Fixtures\EqualNamespacesPerFileWithClassAsFirst');
9856
9857 $this->assertEquals(array(
9858 'secure' => __NAMESPACE__ . '\Fixtures\Annotation\Secure',
9859 'route' => __NAMESPACE__ . '\Fixtures\Annotation\Route',
9860 ), $parser->parseClass($class));
9861 }
9862
9863 public function testEqualNamespacesPerFileWithClassAsLast()
9864 {
9865 $parser = new PhpParser();
9866 $class = new ReflectionClass(__NAMESPACE__ . '\Fixtures\EqualNamespacesPerFileWithClassAsLast');
9867
9868 $this->assertEquals(array(
9869 'route' => __NAMESPACE__ . '\Fixtures\Annotation\Route',
9870 'template' => __NAMESPACE__ . '\Fixtures\Annotation\Template',
9871 ), $parser->parseClass($class));
9872 }
9873
9874 public function testDifferentNamespacesPerFileWithClassAsFirst()
9875 {
9876 $parser = new PhpParser();
9877 $class = new ReflectionClass(__NAMESPACE__ . '\Fixtures\DifferentNamespacesPerFileWithClassAsFirst');
9878
9879 $this->assertEquals(array(
9880 'secure' => __NAMESPACE__ . '\Fixtures\Annotation\Secure',
9881 ), $parser->parseClass($class));
9882 }
9883
9884 public function testDifferentNamespacesPerFileWithClassAsLast()
9885 {
9886 $parser = new PhpParser();
9887 $class = new ReflectionClass(__NAMESPACE__ . '\Fixtures\DifferentNamespacesPerFileWithClassAsLast');
9888
9889 $this->assertEquals(array(
9890 'template' => __NAMESPACE__ . '\Fixtures\Annotation\Template',
9891 ), $parser->parseClass($class));
9892 }
9893
9894 public function testGlobalNamespacesPerFileWithClassAsFirst()
9895 {
9896 $parser = new PhpParser();
9897 $class = new \ReflectionClass('\GlobalNamespacesPerFileWithClassAsFirst');
9898
9899 $this->assertEquals(array(
9900 'secure' => __NAMESPACE__ . '\Fixtures\Annotation\Secure',
9901 'route' => __NAMESPACE__ . '\Fixtures\Annotation\Route',
9902 ), $parser->parseClass($class));
9903 }
9904
9905 public function testGlobalNamespacesPerFileWithClassAsLast()
9906 {
9907 $parser = new PhpParser();
9908 $class = new ReflectionClass('\GlobalNamespacesPerFileWithClassAsLast');
9909
9910 $this->assertEquals(array(
9911 'route' => __NAMESPACE__ . '\Fixtures\Annotation\Route',
9912 'template' => __NAMESPACE__ . '\Fixtures\Annotation\Template',
9913 ), $parser->parseClass($class));
9914 }
9915
9916 public function testNamespaceWithClosureDeclaration()
9917 {
9918 $parser = new PhpParser();
9919 $class = new ReflectionClass(__NAMESPACE__ . '\Fixtures\NamespaceWithClosureDeclaration');
9920
9921 $this->assertEquals(array(
9922 'secure' => __NAMESPACE__ . '\Fixtures\Annotation\Secure',
9923 'route' => __NAMESPACE__ . '\Fixtures\Annotation\Route',
9924 'template' => __NAMESPACE__ . '\Fixtures\Annotation\Template',
9925 ), $parser->parseClass($class));
9926 }
9927
9928 public function testIfPointerResetsOnMultipleParsingTries()
9929 {
9930 $parser = new PhpParser();
9931 $class = new ReflectionClass(__NAMESPACE__ . '\Fixtures\NamespaceWithClosureDeclaration');
9932
9933 $this->assertEquals(array(
9934 'secure' => __NAMESPACE__ . '\Fixtures\Annotation\Secure',
9935 'route' => __NAMESPACE__ . '\Fixtures\Annotation\Route',
9936 'template' => __NAMESPACE__ . '\Fixtures\Annotation\Template',
9937 ), $parser->parseClass($class));
9938
9939 $this->assertEquals(array(
9940 'secure' => __NAMESPACE__ . '\Fixtures\Annotation\Secure',
9941 'route' => __NAMESPACE__ . '\Fixtures\Annotation\Route',
9942 'template' => __NAMESPACE__ . '\Fixtures\Annotation\Template',
9943 ), $parser->parseClass($class));
9944 }
9945
9946 /**
9947 * @group DCOM-97
9948 * @group regression
9949 */
9950 public function testClassWithClosure()
9951 {
9952 $parser = new PhpParser();
9953 $class = new ReflectionClass(__NAMESPACE__ . '\Fixtures\ClassWithClosure');
9954
9955 $this->assertEquals(array(
9956 'annotationtargetall' => __NAMESPACE__ . '\Fixtures\AnnotationTargetAll',
9957 'annotationtargetannotation' => __NAMESPACE__ . '\Fixtures\AnnotationTargetAnnotation',
9958 ), $parser->parseClass($class));
9959 }
9960 }<?php
9961
9962 use Doctrine\Common\Annotations\Annotation;
9963
9964 /** @Annotation */
9965 class TopLevelAnnotation extends Annotation
9966 {
9967 }
9968 <?php
9969
9970 namespace Doctrine\Tests;
9971
9972 /**
9973 * Base testcase class for all Doctrine testcases.
9974 */
9975 abstract class DoctrineTestCase extends \PHPUnit_Framework_TestCase
9976 {
9977 }<?php
9978 /*
9979 * This file bootstraps the test environment.
9980 */
9981 namespace Doctrine\Tests;
9982
9983 error_reporting(E_ALL | E_STRICT);
9984
9985 // register silently failing autoloader
9986 spl_autoload_register(function($class)
9987 {
9988 if (0 === strpos($class, 'Doctrine\Tests\\')) {
9989 $path = __DIR__.'/../../'.strtr($class, '\\', '/').'.php';
9990 if (is_file($path) && is_readable($path)) {
9991 require_once $path;
9992
9993 return true;
9994 }
9995 }
9996 });
9997
9998 require_once __DIR__ . "/../../../vendor/autoload.php";
9999
10000 \Doctrine\Common\Annotations\AnnotationRegistry::registerAutoloadNamespace(
10001 'Doctrine\Tests\Common\Annotations\Fixtures', __DIR__ . '/../../'
10002 );
10003 {
10004 "name": "doctrine/annotations",
10005 "type": "library",
10006 "description": "Docblock Annotations Parser",
10007 "keywords": ["annotations", "docblock", "parser"],
10008 "homepage": "http://www.doctrine-project.org",
10009 "license": "MIT",
10010 "authors": [
10011 {"name": "Guilherme Blanco", "email": "guilhermeblanco@gmail.com"},
10012 {"name": "Roman Borschel", "email": "roman@code-factory.org"},
10013 {"name": "Benjamin Eberlei", "email": "kontakt@beberlei.de"},
10014 {"name": "Jonathan Wage", "email": "jonwage@gmail.com"},
10015 {"name": "Johannes Schmitt", "email": "schmittjoh@gmail.com"}
10016 ],
10017 "require": {
10018 "php": ">=5.3.2",
10019 "doctrine/lexer": "1.*"
10020 },
10021 "require-dev": {
10022 "doctrine/cache": "1.*"
10023 },
10024 "autoload": {
10025 "psr-0": { "Doctrine\\Common\\Annotations\\": "lib/" }
10026 },
10027 "extra": {
10028 "branch-alias": {
10029 "dev-master": "1.0.x-dev"
10030 }
10031 }
10032 }
10033 <?xml version="1.0" encoding="UTF-8"?>
10034
10035 <phpunit backupGlobals="false"
10036 backupStaticAttributes="false"
10037 colors="true"
10038 convertErrorsToExceptions="true"
10039 convertNoticesToExceptions="true"
10040 convertWarningsToExceptions="true"
10041 processIsolation="false"
10042 stopOnFailure="false"
10043 syntaxCheck="false"
10044 bootstrap="./tests/Doctrine/Tests/TestInit.php"
10045 >
10046 <testsuites>
10047 <testsuite name="Doctrine Annotations Test Suite">
10048 <directory>./tests/Doctrine/</directory>
10049 </testsuite>
10050 </testsuites>
10051
10052 <filter>
10053 <whitelist>
10054 <directory>./lib/Doctrine/</directory>
10055 </whitelist>
10056 </filter>
10057
10058 <groups>
10059 <exclude>
10060 <group>performance</group>
10061 </exclude>
10062 </groups>
10063 </phpunit>
10064 # Doctrine Lexer
10065
10066 Base library for a lexer that can be used in Top-Down, Recursive Descent Parsers.
10067
10068 This lexer is used in Doctrine Annotations and in Doctrine ORM (DQL).
10069 <?php
10070 /*
10071 * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
10072 * "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
10073 * LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR
10074 * A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT
10075 * OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
10076 * SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT
10077 * LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
10078 * DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
10079 * THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
10080 * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
10081 * OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
10082 *
10083 * This software consists of voluntary contributions made by many individuals
10084 * and is licensed under the MIT license. For more information, see
10085 * <http://www.doctrine-project.org>.
10086 */
10087
10088 namespace Doctrine\Common\Lexer;
10089
10090 /**
10091 * Base class for writing simple lexers, i.e. for creating small DSLs.
10092 *
10093 * @since 2.0
10094 * @author Guilherme Blanco <guilhermeblanco@hotmail.com>
10095 * @author Jonathan Wage <jonwage@gmail.com>
10096 * @author Roman Borschel <roman@code-factory.org>
10097 */
10098 abstract class AbstractLexer
10099 {
10100 /**
10101 * @var array Array of scanned tokens
10102 */
10103 private $tokens = array();
10104
10105 /**
10106 * @var integer Current lexer position in input string
10107 */
10108 private $position = 0;
10109
10110 /**
10111 * @var integer Current peek of current lexer position
10112 */
10113 private $peek = 0;
10114
10115 /**
10116 * @var array The next token in the input.
10117 */
10118 public $lookahead;
10119
10120 /**
10121 * @var array The last matched/seen token.
10122 */
10123 public $token;
10124
10125 /**
10126 * Sets the input data to be tokenized.
10127 *
10128 * The Lexer is immediately reset and the new input tokenized.
10129 * Any unprocessed tokens from any previous input are lost.
10130 *
10131 * @param string $input The input to be tokenized.
10132 */
10133 public function setInput($input)
10134 {
10135 $this->tokens = array();
10136 $this->reset();
10137 $this->scan($input);
10138 }
10139
10140 /**
10141 * Resets the lexer.
10142 */
10143 public function reset()
10144 {
10145 $this->lookahead = null;
10146 $this->token = null;
10147 $this->peek = 0;
10148 $this->position = 0;
10149 }
10150
10151 /**
10152 * Resets the peek pointer to 0.
10153 */
10154 public function resetPeek()
10155 {
10156 $this->peek = 0;
10157 }
10158
10159 /**
10160 * Resets the lexer position on the input to the given position.
10161 *
10162 * @param integer $position Position to place the lexical scanner
10163 */
10164 public function resetPosition($position = 0)
10165 {
10166 $this->position = $position;
10167 }
10168
10169 /**
10170 * Checks whether a given token matches the current lookahead.
10171 *
10172 * @param integer|string $token
10173 * @return boolean
10174 */
10175 public function isNextToken($token)
10176 {
10177 return null !== $this->lookahead && $this->lookahead['type'] === $token;
10178 }
10179
10180 /**
10181 * Checks whether any of the given tokens matches the current lookahead
10182 *
10183 * @param array $tokens
10184 * @return boolean
10185 */
10186 public function isNextTokenAny(array $tokens)
10187 {
10188 return null !== $this->lookahead && in_array($this->lookahead['type'], $tokens, true);
10189 }
10190
10191 /**
10192 * Moves to the next token in the input string.
10193 *
10194 * A token is an associative array containing three items:
10195 * - 'value' : the string value of the token in the input string
10196 * - 'type' : the type of the token (identifier, numeric, string, input
10197 * parameter, none)
10198 * - 'position' : the position of the token in the input string
10199 *
10200 * @return array|null the next token; null if there is no more tokens left
10201 */
10202 public function moveNext()
10203 {
10204 $this->peek = 0;
10205 $this->token = $this->lookahead;
10206 $this->lookahead = (isset($this->tokens[$this->position]))
10207 ? $this->tokens[$this->position++] : null;
10208
10209 return $this->lookahead !== null;
10210 }
10211
10212 /**
10213 * Tells the lexer to skip input tokens until it sees a token with the given value.
10214 *
10215 * @param string $type The token type to skip until.
10216 */
10217 public function skipUntil($type)
10218 {
10219 while ($this->lookahead !== null && $this->lookahead['type'] !== $type) {
10220 $this->moveNext();
10221 }
10222 }
10223
10224 /**
10225 * Checks if given value is identical to the given token
10226 *
10227 * @param mixed $value
10228 * @param integer $token
10229 * @return boolean
10230 */
10231 public function isA($value, $token)
10232 {
10233 return $this->getType($value) === $token;
10234 }
10235
10236 /**
10237 * Moves the lookahead token forward.
10238 *
10239 * @return array | null The next token or NULL if there are no more tokens ahead.
10240 */
10241 public function peek()
10242 {
10243 if (isset($this->tokens[$this->position + $this->peek])) {
10244 return $this->tokens[$this->position + $this->peek++];
10245 } else {
10246 return null;
10247 }
10248 }
10249
10250 /**
10251 * Peeks at the next token, returns it and immediately resets the peek.
10252 *
10253 * @return array|null The next token or NULL if there are no more tokens ahead.
10254 */
10255 public function glimpse()
10256 {
10257 $peek = $this->peek();
10258 $this->peek = 0;
10259 return $peek;
10260 }
10261
10262 /**
10263 * Scans the input string for tokens.
10264 *
10265 * @param string $input a query string
10266 */
10267 protected function scan($input)
10268 {
10269 static $regex;
10270
10271 if ( ! isset($regex)) {
10272 $regex = '/(' . implode(')|(', $this->getCatchablePatterns()) . ')|'
10273 . implode('|', $this->getNonCatchablePatterns()) . '/i';
10274 }
10275
10276 $flags = PREG_SPLIT_NO_EMPTY | PREG_SPLIT_DELIM_CAPTURE | PREG_SPLIT_OFFSET_CAPTURE;
10277 $matches = preg_split($regex, $input, -1, $flags);
10278
10279 foreach ($matches as $match) {
10280 // Must remain before 'value' assignment since it can change content
10281 $type = $this->getType($match[0]);
10282
10283 $this->tokens[] = array(
10284 'value' => $match[0],
10285 'type' => $type,
10286 'position' => $match[1],
10287 );
10288 }
10289 }
10290
10291 /**
10292 * Gets the literal for a given token.
10293 *
10294 * @param integer $token
10295 * @return string
10296 */
10297 public function getLiteral($token)
10298 {
10299 $className = get_class($this);
10300 $reflClass = new \ReflectionClass($className);
10301 $constants = $reflClass->getConstants();
10302
10303 foreach ($constants as $name => $value) {
10304 if ($value === $token) {
10305 return $className . '::' . $name;
10306 }
10307 }
10308
10309 return $token;
10310 }
10311
10312 /**
10313 * Lexical catchable patterns.
10314 *
10315 * @return array
10316 */
10317 abstract protected function getCatchablePatterns();
10318
10319 /**
10320 * Lexical non-catchable patterns.
10321 *
10322 * @return array
10323 */
10324 abstract protected function getNonCatchablePatterns();
10325
10326 /**
10327 * Retrieve token type. Also processes the token value if necessary.
10328 *
10329 * @param string $value
10330 * @return integer
10331 */
10332 abstract protected function getType(&$value);
10333 }
10334 {
10335 "name": "doctrine/lexer",
10336 "type": "library",
10337 "description": "Base library for a lexer that can be used in Top-Down, Recursive Descent Parsers.",
10338 "keywords": ["lexer", "parser"],
10339 "homepage": "http://www.doctrine-project.org",
10340 "license": "MIT",
10341 "authors": [
10342 {"name": "Guilherme Blanco", "email": "guilhermeblanco@gmail.com"},
10343 {"name": "Roman Borschel", "email": "roman@code-factory.org"},
10344 {"name": "Johannes Schmitt", "email": "schmittjoh@gmail.com"}
10345 ],
10346 "require": {
10347 "php": ">=5.3.2"
10348 },
10349 "autoload": {
10350 "psr-0": { "Doctrine\\Common\\Lexer\\": "lib/" }
10351 }
10352 }
10353 <?php
10354
10355 Phar::mapPhar('guzzle.phar');
10356
10357 require_once 'phar://guzzle.phar/vendor/symfony/class-loader/Symfony/Component/ClassLoader/UniversalClassLoader.php';
10358
10359 $classLoader = new Symfony\Component\ClassLoader\UniversalClassLoader();
10360 $classLoader->registerNamespaces(array(
10361 'Guzzle' => 'phar://guzzle.phar/src',
10362 'Symfony\\Component\\EventDispatcher' => 'phar://guzzle.phar/vendor/symfony/event-dispatcher',
10363 'Doctrine' => 'phar://guzzle.phar/vendor/doctrine/common/lib',
10364 'Monolog' => 'phar://guzzle.phar/vendor/monolog/monolog/src'
10365 ));
10366 $classLoader->register();
10367
10368 __HALT_COMPILER();
10369 Guzzle, PHP HTTP client and webservice framework
10370 ================================================
10371
10372 [![Latest Stable Version](https://poser.pugx.org/guzzle/guzzle/version.png)](https://packagist.org/packages/guzzle/guzzle) [![Composer Downloads](https://poser.pugx.org/guzzle/guzzle/d/total.png)](https://packagist.org/packages/guzzle/guzzle) [![Build Status](https://secure.travis-ci.org/guzzle/guzzle.png?branch=master)](http://travis-ci.org/guzzle/guzzle)
10373
10374 Guzzle is a PHP HTTP client and framework for building RESTful web service clients.
10375
10376 - Extremely powerful API provides all the power of cURL with a simple interface.
10377 - Truly take advantage of HTTP/1.1 with persistent connections, connection pooling, and parallel requests.
10378 - Service description DSL allows you build awesome web service clients faster.
10379 - Symfony2 event-based plugin system allows you to completely modify the behavior of a request.
10380
10381 Get answers with: [Documentation](http://www.guzzlephp.org/), [Forums](https://groups.google.com/forum/?hl=en#!forum/guzzle), IRC ([#guzzlephp](irc://irc.freenode.net/#guzzlephp) @ irc.freenode.net)
10382
10383 ```php
10384 // Really simple using a static facade
10385 Guzzle\Http\StaticClient::mount();
10386 $response = Guzzle::get('http://guzzlephp.org');
10387
10388 // More control using a client class
10389 $client = new \Guzzle\Http\Client('http://guzzlephp.org');
10390 $request = $client->get('/');
10391 $response = $request->send();
10392 ```
10393
10394 ### Installing via Composer
10395
10396 The recommended way to install Guzzle is through [Composer](http://getcomposer.org).
10397
10398 ```bash
10399 # Install Composer
10400 curl -sS https://getcomposer.org/installer | php
10401
10402 # Add Guzzle as a dependency
10403 php composer.phar require guzzle/guzzle:~3.7
10404 ```
10405
10406 After installing, you need to require Composer's autoloader:
10407
10408 ```php
10409 require 'vendor/autoload.php';
10410 ```
10411
10412 ### Installing via phar
10413
10414 [Download the phar](http://guzzlephp.org/guzzle.phar) and include it in your project
10415 ([minimal phar](http://guzzlephp.org/guzzle-min.phar))
10416
10417 Features
10418 --------
10419
10420 - Supports GET, HEAD, POST, DELETE, PUT, PATCH, OPTIONS, and any other custom HTTP method
10421 - Allows full access to request and response headers
10422 - Persistent connections are implicitly managed by Guzzle, resulting in huge performance benefits
10423 - [Send requests in parallel](http://guzzlephp.org/tour/http.html#send-http-requests-in-parallel)
10424 - Cookie sessions can be maintained between requests using the
10425 [CookiePlugin](http://guzzlephp.org/tour/http.html#cookie-session-plugin)
10426 - Allows custom [entity bodies](http://guzzlephp.org/tour/http.html#entity-bodies), including sending data from a PHP
10427 stream and downloading data to a PHP stream
10428 - Responses can be cached and served from cache using the
10429 [caching forward proxy plugin](http://guzzlephp.org/tour/http.html#php-based-caching-forward-proxy)
10430 - Failed requests can be retried using
10431 [truncated exponential backoff](http://guzzlephp.org/tour/http.html#truncated-exponential-backoff) with custom retry
10432 policies
10433 - Entity bodies can be validated automatically using Content-MD5 headers and the
10434 [MD5 hash validator plugin](http://guzzlephp.org/tour/http.html#md5-hash-validator-plugin)
10435 - All data sent over the wire can be logged using the
10436 [LogPlugin](http://guzzlephp.org/tour/http.html#over-the-wire-logging)
10437 - Subject/Observer signal slot system for unobtrusively
10438 [modifying request behavior](http://guzzlephp.org/guide/http/creating_plugins.html)
10439 - Supports all of the features of libcurl including authentication, compression, redirects, SSL, proxies, etc
10440 - Web service client framework for building future-proof interfaces to web services
10441 - Includes a [service description DSL](http://guzzlephp.org/guide/service/service_descriptions.html) for quickly
10442 building webservice clients
10443 - Full support for [URI templates](http://tools.ietf.org/html/rfc6570)
10444 - Advanced batching functionality to efficiently send requests or commands in parallel with customizable batch sizes
10445 and transfer strategies
10446
10447 HTTP basics
10448 -----------
10449
10450 ```php
10451 <?php
10452
10453 use Guzzle\Http\Client;
10454
10455 $client = new Client('http://www.example.com/api/v1/key/{key}', [
10456 'key' => '***'
10457 ]);
10458
10459 // Issue a path using a relative URL to the client's base URL
10460 // Sends to http://www.example.com/api/v1/key/***/users
10461 $request = $client->get('users');
10462 $response = $request->send();
10463
10464 // Relative URL that overwrites the path of the base URL
10465 $request = $client->get('/test/123.php?a=b');
10466
10467 // Issue a head request on the base URL
10468 $response = $client->head()->send();
10469 // Delete user 123
10470 $response = $client->delete('users/123')->send();
10471
10472 // Send a PUT request with custom headers
10473 $response = $client->put('upload/text', [
10474 'X-Header' => 'My Header'
10475 ], 'body of the request')->send();
10476
10477 // Send a PUT request using the contents of a PHP stream as the body
10478 // Send using an absolute URL (overrides the base URL)
10479 $response = $client->put('http://www.example.com/upload', [
10480 'X-Header' => 'My Header'
10481 ], fopen('http://www.test.com/', 'r'));
10482
10483 // Create a POST request with a file upload (notice the @ symbol):
10484 $request = $client->post('http://localhost:8983/solr/update', null, [
10485 'custom_field' => 'my value',
10486 'file' => '@/path/to/documents.xml'
10487 ]);
10488
10489 // Create a POST request and add the POST files manually
10490 $request = $client->post('http://localhost:8983/solr/update')
10491 ->addPostFiles(['file' => '/path/to/documents.xml']);
10492
10493 // Responses are objects
10494 echo $response->getStatusCode() . ' ' . $response->getReasonPhrase() . "\n";
10495
10496 // Requests and responses can be cast to a string to show the raw HTTP message
10497 echo $request . "\n\n" . $response;
10498
10499 // Create a request based on an HTTP message
10500 $request = RequestFactory::fromMessage(
10501 "PUT / HTTP/1.1\r\n" .
10502 "Host: test.com:8081\r\n" .
10503 "Content-Type: text/plain" .
10504 "Transfer-Encoding: chunked\r\n" .
10505 "\r\n" .
10506 "this is the body"
10507 );
10508 ```
10509
10510 Using the static client facade
10511 ------------------------------
10512
10513 You can use Guzzle through a static client to make it even easier to send simple HTTP requests.
10514
10515 ```php
10516 <?php
10517
10518 // Use the static client directly:
10519 $response = Guzzle\Http\StaticClient::get('http://www.google.com');
10520
10521 // Or, mount the client to \Guzzle to make it easier to use
10522 Guzzle\Http\StaticClient::mount();
10523
10524 $response = Guzzle::get('http://guzzlephp.org');
10525
10526 // Custom options can be passed into requests created by the static client
10527 $response = Guzzle::post('http://guzzlephp.org', [
10528 'headers' => ['X-Foo' => 'Bar']
10529 'body' => ['Foo' => 'Bar'],
10530 'query' => ['Test' => 123],
10531 'timeout' => 10,
10532 'debug' => true,
10533 'save_to' => '/path/to/file.html'
10534 ]);
10535 ```
10536
10537 ### Available request options:
10538
10539 * headers: Associative array of headers
10540 * query: Associative array of query string values to add to the request
10541 * body: Body of a request, including an EntityBody, string, or array when sending POST requests. Setting a body for a
10542 GET request will set where the response body is downloaded.
10543 * auth: Array of HTTP authentication parameters to use with the request. The array must contain the
10544 username in index [0], the password in index [1], and can optionally contain the authentication type in index [2].
10545 The authentication types are: "Basic", "Digest". The default auth type is "Basic".
10546 * cookies: Associative array of cookies
10547 * allow_redirects: Set to false to disable redirects
10548 * save_to: String, fopen resource, or EntityBody object used to store the body of the response
10549 * events: Associative array mapping event names to a closure or array of (priority, closure)
10550 * plugins: Array of plugins to add to the request
10551 * exceptions: Set to false to disable throwing exceptions on an HTTP level error (e.g. 404, 500, etc)
10552 * timeout: Float describing the timeout of the request in seconds
10553 * connect_timeout: Float describing the number of seconds to wait while trying to connect. Use 0 to wait
10554 indefinitely.
10555 * verify: Set to true to enable SSL cert validation (the default), false to disable, or supply the path to a CA
10556 bundle to enable verification using a custom certificate.
10557 * proxy: Specify an HTTP proxy (e.g. "http://username:password@192.168.16.1:10")
10558 * debug: Set to true to display all data sent over the wire
10559
10560 These options can also be used when creating requests using a standard client:
10561
10562 ```php
10563 $client = new Guzzle\Http\Client();
10564 // Create a request with a timeout of 10 seconds
10565 $request = $client->get('http://guzzlephp.org', [], ['timeout' => 10]);
10566 $response = $request->send();
10567 ```
10568
10569 Unit testing
10570 ------------
10571
10572 Guzzle uses PHPUnit for unit testing. In order to run the unit tests, you'll first need
10573 to install the dependencies of the project using Composer: `php composer.phar install --dev`.
10574 You can then run the tests using `vendor/bin/phpunit`.
10575
10576 If you are running the tests with xdebug enabled, you may encounter the following issue: 'Fatal error: Maximum function nesting level of '100' reached, aborting!'. This can be resolved by adding 'xdebug.max_nesting_level = 200' to your php.ini file.
10577
10578 The PECL extensions, uri_template and pecl_http will be required to ensure all the tests can run.
10579 Copyright (c) 2011 Michael Dowling, https://github.com/mtdowling <mtdowling@gmail.com>
10580
10581 Permission is hereby granted, free of charge, to any person obtaining a copy
10582 of this software and associated documentation files (the "Software"), to deal
10583 in the Software without restriction, including without limitation the rights
10584 to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
10585 copies of the Software, and to permit persons to whom the Software is
10586 furnished to do so, subject to the following conditions:
10587
10588 The above copyright notice and this permission notice shall be included in
10589 all copies or substantial portions of the Software.
10590
10591 THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
10592 IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
10593 FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
10594 AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
10595 LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
10596 OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
10597 THE SOFTWARE.
10598 <?php
10599
10600 namespace Guzzle\Cache;
10601
10602 use Doctrine\Common\Cache\Cache;
10603 use Guzzle\Common\Version;
10604 use Guzzle\Common\Exception\InvalidArgumentException;
10605 use Guzzle\Common\FromConfigInterface;
10606 use Zend\Cache\Storage\StorageInterface;
10607
10608 /**
10609 * Generates cache adapters from any number of known cache implementations
10610 */
10611 class CacheAdapterFactory implements FromConfigInterface
10612 {
10613 /**
10614 * Create a Guzzle cache adapter based on an array of options
10615 *
10616 * @param mixed $cache Cache value
10617 *
10618 * @return CacheAdapterInterface
10619 * @throws InvalidArgumentException
10620 */
10621 public static function fromCache($cache)
10622 {
10623 if (!is_object($cache)) {
10624 throw new InvalidArgumentException('Cache must be one of the known cache objects');
10625 }
10626
10627 if ($cache instanceof CacheAdapterInterface) {
10628 return $cache;
10629 } elseif ($cache instanceof Cache) {
10630 return new DoctrineCacheAdapter($cache);
10631 } elseif ($cache instanceof StorageInterface) {
10632 return new Zf2CacheAdapter($cache);
10633 } else {
10634 throw new InvalidArgumentException('Unknown cache type: ' . get_class($cache));
10635 }
10636 }
10637
10638 /**
10639 * Create a Guzzle cache adapter based on an array of options
10640 *
10641 * @param array $config Array of configuration options
10642 *
10643 * @return CacheAdapterInterface
10644 * @throws InvalidArgumentException
10645 * @deprecated This will be removed in a future version
10646 * @codeCoverageIgnore
10647 */
10648 public static function factory($config = array())
10649 {
10650 Version::warn(__METHOD__ . ' is deprecated');
10651 if (!is_array($config)) {
10652 throw new InvalidArgumentException('$config must be an array');
10653 }
10654
10655 if (!isset($config['cache.adapter']) && !isset($config['cache.provider'])) {
10656 $config['cache.adapter'] = 'Guzzle\Cache\NullCacheAdapter';
10657 $config['cache.provider'] = null;
10658 } else {
10659 // Validate that the options are valid
10660 foreach (array('cache.adapter', 'cache.provider') as $required) {
10661 if (!isset($config[$required])) {
10662 throw new InvalidArgumentException("{$required} is a required CacheAdapterFactory option");
10663 }
10664 if (is_string($config[$required])) {
10665 // Convert dot notation to namespaces
10666 $config[$required] = str_replace('.', '\\', $config[$required]);
10667 if (!class_exists($config[$required])) {
10668 throw new InvalidArgumentException("{$config[$required]} is not a valid class for {$required}");
10669 }
10670 }
10671 }
10672 // Instantiate the cache provider
10673 if (is_string($config['cache.provider'])) {
10674 $args = isset($config['cache.provider.args']) ? $config['cache.provider.args'] : null;
10675 $config['cache.provider'] = self::createObject($config['cache.provider'], $args);
10676 }
10677 }
10678
10679 // Instantiate the cache adapter using the provider and options
10680 if (is_string($config['cache.adapter'])) {
10681 $args = isset($config['cache.adapter.args']) ? $config['cache.adapter.args'] : array();
10682 array_unshift($args, $config['cache.provider']);
10683 $config['cache.adapter'] = self::createObject($config['cache.adapter'], $args);
10684 }
10685
10686 return $config['cache.adapter'];
10687 }
10688
10689 /**
10690 * Create a class using an array of constructor arguments
10691 *
10692 * @param string $className Class name
10693 * @param array $args Arguments for the class constructor
10694 *
10695 * @return mixed
10696 * @throws RuntimeException
10697 * @deprecated
10698 * @codeCoverageIgnore
10699 */
10700 private static function createObject($className, array $args = null)
10701 {
10702 try {
10703 if (!$args) {
10704 return new $className;
10705 } else {
10706 $c = new \ReflectionClass($className);
10707 return $c->newInstanceArgs($args);
10708 }
10709 } catch (\Exception $e) {
10710 throw new RuntimeException($e->getMessage(), $e->getCode(), $e);
10711 }
10712 }
10713 }
10714 <?php
10715
10716 namespace Guzzle\Cache;
10717
10718 /**
10719 * Interface for cache adapters.
10720 *
10721 * Cache adapters allow Guzzle to utilize various frameworks for caching HTTP responses.
10722 *
10723 * @link http://www.doctrine-project.org/ Inspired by Doctrine 2
10724 */
10725 interface CacheAdapterInterface
10726 {
10727 /**
10728 * Test if an entry exists in the cache.
10729 *
10730 * @param string $id cache id The cache id of the entry to check for.
10731 * @param array $options Array of cache adapter options
10732 *
10733 * @return bool Returns TRUE if a cache entry exists for the given cache id, FALSE otherwise.
10734 */
10735 public function contains($id, array $options = null);
10736
10737 /**
10738 * Deletes a cache entry.
10739 *
10740 * @param string $id cache id
10741 * @param array $options Array of cache adapter options
10742 *
10743 * @return bool TRUE on success, FALSE on failure
10744 */
10745 public function delete($id, array $options = null);
10746
10747 /**
10748 * Fetches an entry from the cache.
10749 *
10750 * @param string $id cache id The id of the cache entry to fetch.
10751 * @param array $options Array of cache adapter options
10752 *
10753 * @return string The cached data or FALSE, if no cache entry exists for the given id.
10754 */
10755 public function fetch($id, array $options = null);
10756
10757 /**
10758 * Puts data into the cache.
10759 *
10760 * @param string $id The cache id
10761 * @param string $data The cache entry/data
10762 * @param int|bool $lifeTime The lifetime. If != false, sets a specific lifetime for this cache entry
10763 * @param array $options Array of cache adapter options
10764 *
10765 * @return bool TRUE if the entry was successfully stored in the cache, FALSE otherwise.
10766 */
10767 public function save($id, $data, $lifeTime = false, array $options = null);
10768 }
10769 <?php
10770
10771 namespace Guzzle\Cache;
10772
10773 /**
10774 * Null cache adapter
10775 */
10776 class NullCacheAdapter extends AbstractCacheAdapter
10777 {
10778 public function __construct() {}
10779
10780 public function contains($id, array $options = null)
10781 {
10782 return false;
10783 }
10784
10785 public function delete($id, array $options = null)
10786 {
10787 return true;
10788 }
10789
10790 public function fetch($id, array $options = null)
10791 {
10792 return false;
10793 }
10794
10795 public function save($id, $data, $lifeTime = false, array $options = null)
10796 {
10797 return true;
10798 }
10799 }
10800 <?php
10801
10802 namespace Guzzle\Cache;
10803
10804 /**
10805 * Cache adapter that defers to closures for implementation
10806 */
10807 class ClosureCacheAdapter implements CacheAdapterInterface
10808 {
10809 /**
10810 * @var array Mapping of method names to callables
10811 */
10812 protected $callables;
10813
10814 /**
10815 * The callables array is an array mapping the actions of the cache adapter to callables.
10816 * - contains: Callable that accepts an $id and $options argument
10817 * - delete: Callable that accepts an $id and $options argument
10818 * - fetch: Callable that accepts an $id and $options argument
10819 * - save: Callable that accepts an $id, $data, $lifeTime, and $options argument
10820 *
10821 * @param array $callables array of action names to callable
10822 *
10823 * @throws \InvalidArgumentException if the callable is not callable
10824 */
10825 public function __construct(array $callables)
10826 {
10827 // Validate each key to ensure it exists and is callable
10828 foreach (array('contains', 'delete', 'fetch', 'save') as $key) {
10829 if (!array_key_exists($key, $callables) || !is_callable($callables[$key])) {
10830 throw new \InvalidArgumentException("callables must contain a callable {$key} key");
10831 }
10832 }
10833
10834 $this->callables = $callables;
10835 }
10836
10837 public function contains($id, array $options = null)
10838 {
10839 return call_user_func($this->callables['contains'], $id, $options);
10840 }
10841
10842 public function delete($id, array $options = null)
10843 {
10844 return call_user_func($this->callables['delete'], $id, $options);
10845 }
10846
10847 public function fetch($id, array $options = null)
10848 {
10849 return call_user_func($this->callables['fetch'], $id, $options);
10850 }
10851
10852 public function save($id, $data, $lifeTime = false, array $options = null)
10853 {
10854 return call_user_func($this->callables['save'], $id, $data, $lifeTime, $options);
10855 }
10856 }
10857 <?php
10858
10859 namespace Guzzle\Cache;
10860
10861 use Zend\Cache\Storage\StorageInterface;
10862
10863 /**
10864 * Zend Framework 2 cache adapter
10865 *
10866 * @link http://packages.zendframework.com/docs/latest/manual/en/zend.cache.html
10867 */
10868 class Zf2CacheAdapter extends AbstractCacheAdapter
10869 {
10870 /**
10871 * @param StorageInterface $cache Zend Framework 2 cache adapter
10872 */
10873 public function __construct(StorageInterface $cache)
10874 {
10875 $this->cache = $cache;
10876 }
10877
10878 public function contains($id, array $options = null)
10879 {
10880 return $this->cache->hasItem($id);
10881 }
10882
10883 public function delete($id, array $options = null)
10884 {
10885 return $this->cache->removeItem($id);
10886 }
10887
10888 public function fetch($id, array $options = null)
10889 {
10890 return $this->cache->getItem($id);
10891 }
10892
10893 public function save($id, $data, $lifeTime = false, array $options = null)
10894 {
10895 return $this->cache->setItem($id, $data);
10896 }
10897 }
10898 <?php
10899
10900 namespace Guzzle\Cache;
10901
10902 /**
10903 * Abstract cache adapter
10904 */
10905 abstract class AbstractCacheAdapter implements CacheAdapterInterface
10906 {
10907 protected $cache;
10908
10909 /**
10910 * Get the object owned by the adapter
10911 *
10912 * @return mixed
10913 */
10914 public function getCacheObject()
10915 {
10916 return $this->cache;
10917 }
10918 }
10919 {
10920 "name": "guzzle/cache",
10921 "description": "Guzzle cache adapter component",
10922 "homepage": "http://guzzlephp.org/",
10923 "keywords": ["cache", "adapter", "zf", "doctrine", "guzzle"],
10924 "license": "MIT",
10925 "authors": [
10926 {
10927 "name": "Michael Dowling",
10928 "email": "mtdowling@gmail.com",
10929 "homepage": "https://github.com/mtdowling"
10930 }
10931 ],
10932 "require": {
10933 "php": ">=5.3.2",
10934 "guzzle/common": "self.version"
10935 },
10936 "autoload": {
10937 "psr-0": { "Guzzle\\Cache": "" }
10938 },
10939 "target-dir": "Guzzle/Cache",
10940 "extra": {
10941 "branch-alias": {
10942 "dev-master": "3.7-dev"
10943 }
10944 }
10945 }
10946 <?php
10947
10948 namespace Guzzle\Cache;
10949
10950 use Guzzle\Common\Version;
10951
10952 /**
10953 * Zend Framework 1 cache adapter
10954 *
10955 * @link http://framework.zend.com/manual/en/zend.cache.html
10956 * @deprecated
10957 * @codeCoverageIgnore
10958 */
10959 class Zf1CacheAdapter extends AbstractCacheAdapter
10960 {
10961 /**
10962 * @param \Zend_Cache_Backend $cache Cache object to wrap
10963 */
10964 public function __construct(\Zend_Cache_Backend $cache)
10965 {
10966 Version::warn(__CLASS__ . ' is deprecated. Upgrade to ZF2 or use PsrCacheAdapter');
10967 $this->cache = $cache;
10968 }
10969
10970 public function contains($id, array $options = null)
10971 {
10972 return $this->cache->test($id);
10973 }
10974
10975 public function delete($id, array $options = null)
10976 {
10977 return $this->cache->remove($id);
10978 }
10979
10980 public function fetch($id, array $options = null)
10981 {
10982 return $this->cache->load($id);
10983 }
10984
10985 public function save($id, $data, $lifeTime = false, array $options = null)
10986 {
10987 return $this->cache->save($data, $id, array(), $lifeTime);
10988 }
10989 }
10990 <?php
10991
10992 namespace Guzzle\Cache;
10993
10994 use Doctrine\Common\Cache\Cache;
10995
10996 /**
10997 * Doctrine 2 cache adapter
10998 *
10999 * @link http://www.doctrine-project.org/
11000 */
11001 class DoctrineCacheAdapter extends AbstractCacheAdapter
11002 {
11003 /**
11004 * @param Cache $cache Doctrine cache object
11005 */
11006 public function __construct(Cache $cache)
11007 {
11008 $this->cache = $cache;
11009 }
11010
11011 public function contains($id, array $options = null)
11012 {
11013 return $this->cache->contains($id);
11014 }
11015
11016 public function delete($id, array $options = null)
11017 {
11018 return $this->cache->delete($id);
11019 }
11020
11021 public function fetch($id, array $options = null)
11022 {
11023 return $this->cache->fetch($id);
11024 }
11025
11026 public function save($id, $data, $lifeTime = false, array $options = null)
11027 {
11028 return $this->cache->save($id, $data, $lifeTime);
11029 }
11030 }
11031 <?php
11032
11033 namespace Guzzle\Log;
11034
11035 /**
11036 * Adapter class that allows Guzzle to log data using various logging implementations
11037 */
11038 abstract class AbstractLogAdapter implements LogAdapterInterface
11039 {
11040 protected $log;
11041
11042 public function getLogObject()
11043 {
11044 return $this->log;
11045 }
11046 }
11047 <?php
11048
11049 namespace Guzzle\Log;
11050
11051 /**
11052 * Stores all log messages in an array
11053 */
11054 class ArrayLogAdapter implements LogAdapterInterface
11055 {
11056 protected $logs = array();
11057
11058 public function log($message, $priority = LOG_INFO, $extras = array())
11059 {
11060 $this->logs[] = array('message' => $message, 'priority' => $priority, 'extras' => $extras);
11061 }
11062
11063 /**
11064 * Get logged entries
11065 *
11066 * @return array
11067 */
11068 public function getLogs()
11069 {
11070 return $this->logs;
11071 }
11072
11073 /**
11074 * Clears logged entries
11075 */
11076 public function clearLogs()
11077 {
11078 $this->logs = array();
11079 }
11080 }
11081 <?php
11082
11083 namespace Guzzle\Log;
11084
11085 /**
11086 * Logs messages using Closures. Closures combined with filtering can trigger application events based on log messages.
11087 */
11088 class ClosureLogAdapter extends AbstractLogAdapter
11089 {
11090 public function __construct($logObject)
11091 {
11092 if (!is_callable($logObject)) {
11093 throw new \InvalidArgumentException('Object must be callable');
11094 }
11095
11096 $this->log = $logObject;
11097 }
11098
11099 public function log($message, $priority = LOG_INFO, $extras = array())
11100 {
11101 call_user_func($this->log, $message, $priority, $extras);
11102 }
11103 }
11104 <?php
11105
11106 namespace Guzzle\Log;
11107
11108 use Psr\Log\LogLevel;
11109 use Psr\Log\LoggerInterface;
11110
11111 /**
11112 * PSR-3 log adapter
11113 *
11114 * @link https://github.com/php-fig/fig-standards/blob/master/accepted/PSR-3-logger-interface.md
11115 */
11116 class PsrLogAdapter extends AbstractLogAdapter
11117 {
11118 /**
11119 * syslog to PSR-3 mappings
11120 */
11121 private static $mapping = array(
11122 LOG_DEBUG => LogLevel::DEBUG,
11123 LOG_INFO => LogLevel::INFO,
11124 LOG_WARNING => LogLevel::WARNING,
11125 LOG_ERR => LogLevel::ERROR,
11126 LOG_CRIT => LogLevel::CRITICAL,
11127 LOG_ALERT => LogLevel::ALERT
11128 );
11129
11130 public function __construct(LoggerInterface $logObject)
11131 {
11132 $this->log = $logObject;
11133 }
11134
11135 public function log($message, $priority = LOG_INFO, $extras = array())
11136 {
11137 $this->log->log(self::$mapping[$priority], $message, $extras);
11138 }
11139 }
11140 <?php
11141
11142 namespace Guzzle\Log;
11143
11144 use Guzzle\Http\Curl\CurlHandle;
11145 use Guzzle\Http\Message\RequestInterface;
11146 use Guzzle\Http\Message\EntityEnclosingRequestInterface;
11147 use Guzzle\Http\Message\Response;
11148
11149 /**
11150 * Message formatter used in various places in the framework
11151 *
11152 * Format messages using a template that can contain the the following variables:
11153 *
11154 * - {request}: Full HTTP request message
11155 * - {response}: Full HTTP response message
11156 * - {ts}: Timestamp
11157 * - {host}: Host of the request
11158 * - {method}: Method of the request
11159 * - {url}: URL of the request
11160 * - {host}: Host of the request
11161 * - {protocol}: Request protocol
11162 * - {version}: Protocol version
11163 * - {resource}: Resource of the request (path + query + fragment)
11164 * - {port}: Port of the request
11165 * - {hostname}: Hostname of the machine that sent the request
11166 * - {code}: Status code of the response (if available)
11167 * - {phrase}: Reason phrase of the response (if available)
11168 * - {curl_error}: Curl error message (if available)
11169 * - {curl_code}: Curl error code (if available)
11170 * - {curl_stderr}: Curl standard error (if available)
11171 * - {connect_time}: Time in seconds it took to establish the connection (if available)
11172 * - {total_time}: Total transaction time in seconds for last transfer (if available)
11173 * - {req_header_*}: Replace `*` with the lowercased name of a request header to add to the message
11174 * - {res_header_*}: Replace `*` with the lowercased name of a response header to add to the message
11175 * - {req_body}: Request body
11176 * - {res_body}: Response body
11177 */
11178 class MessageFormatter
11179 {
11180 const DEFAULT_FORMAT = "{hostname} {req_header_User-Agent} - [{ts}] \"{method} {resource} {protocol}/{version}\" {code} {res_header_Content-Length}";
11181 const DEBUG_FORMAT = ">>>>>>>>\n{request}\n<<<<<<<<\n{response}\n--------\n{curl_stderr}";
11182 const SHORT_FORMAT = '[{ts}] "{method} {resource} {protocol}/{version}" {code}';
11183
11184 /**
11185 * @var string Template used to format log messages
11186 */
11187 protected $template;
11188
11189 /**
11190 * @param string $template Log message template
11191 */
11192 public function __construct($template = self::DEFAULT_FORMAT)
11193 {
11194 $this->template = $template ?: self::DEFAULT_FORMAT;
11195 }
11196
11197 /**
11198 * Set the template to use for logging
11199 *
11200 * @param string $template Log message template
11201 *
11202 * @return self
11203 */
11204 public function setTemplate($template)
11205 {
11206 $this->template = $template;
11207
11208 return $this;
11209 }
11210
11211 /**
11212 * Returns a formatted message
11213 *
11214 * @param RequestInterface $request Request that was sent
11215 * @param Response $response Response that was received
11216 * @param CurlHandle $handle Curl handle associated with the message
11217 * @param array $customData Associative array of custom template data
11218 *
11219 * @return string
11220 */
11221 public function format(
11222 RequestInterface $request,
11223 Response $response = null,
11224 CurlHandle $handle = null,
11225 array $customData = array()
11226 ) {
11227 $cache = $customData;
11228
11229 return preg_replace_callback(
11230 '/{\s*([A-Za-z_\-\.0-9]+)\s*}/',
11231 function (array $matches) use ($request, $response, $handle, &$cache) {
11232
11233 if (array_key_exists($matches[1], $cache)) {
11234 return $cache[$matches[1]];
11235 }
11236
11237 $result = '';
11238 switch ($matches[1]) {
11239 case 'request':
11240 $result = (string) $request;
11241 break;
11242 case 'response':
11243 $result = (string) $response;
11244 break;
11245 case 'req_body':
11246 $result = $request instanceof EntityEnclosingRequestInterface
11247 ? (string) $request->getBody() : '';
11248 break;
11249 case 'res_body':
11250 $result = $response ? $response->getBody(true) : '';
11251 break;
11252 case 'ts':
11253 $result = gmdate('c');
11254 break;
11255 case 'method':
11256 $result = $request->getMethod();
11257 break;
11258 case 'url':
11259 $result = (string) $request->getUrl();
11260 break;
11261 case 'resource':
11262 $result = $request->getResource();
11263 break;
11264 case 'protocol':
11265 $result = 'HTTP';
11266 break;
11267 case 'version':
11268 $result = $request->getProtocolVersion();
11269 break;
11270 case 'host':
11271 $result = $request->getHost();
11272 break;
11273 case 'hostname':
11274 $result = gethostname();
11275 break;
11276 case 'port':
11277 $result = $request->getPort();
11278 break;
11279 case 'code':
11280 $result = $response ? $response->getStatusCode() : '';
11281 break;
11282 case 'phrase':
11283 $result = $response ? $response->getReasonPhrase() : '';
11284 break;
11285 case 'connect_time':
11286 $result = $handle && $handle->getInfo(CURLINFO_CONNECT_TIME)
11287 ? $handle->getInfo(CURLINFO_CONNECT_TIME)
11288 : ($response ? $response->getInfo('connect_time') : '');
11289 break;
11290 case 'total_time':
11291 $result = $handle && $handle->getInfo(CURLINFO_TOTAL_TIME)
11292 ? $handle->getInfo(CURLINFO_TOTAL_TIME)
11293 : ($response ? $response->getInfo('total_time') : '');
11294 break;
11295 case 'curl_error':
11296 $result = $handle ? $handle->getError() : '';
11297 break;
11298 case 'curl_code':
11299 $result = $handle ? $handle->getErrorNo() : '';
11300 break;
11301 case 'curl_stderr':
11302 $result = $handle ? $handle->getStderr() : '';
11303 break;
11304 default:
11305 if (strpos($matches[1], 'req_header_') === 0) {
11306 $result = $request->getHeader(substr($matches[1], 11));
11307 } elseif ($response && strpos($matches[1], 'res_header_') === 0) {
11308 $result = $response->getHeader(substr($matches[1], 11));
11309 }
11310 }
11311
11312 $cache[$matches[1]] = $result;
11313 return $result;
11314 },
11315 $this->template
11316 );
11317 }
11318 }
11319 <?php
11320
11321 namespace Guzzle\Log;
11322
11323 use Guzzle\Common\Version;
11324
11325 /**
11326 * Adapts a Zend Framework 1 logger object
11327 * @deprecated
11328 * @codeCoverageIgnore
11329 */
11330 class Zf1LogAdapter extends AbstractLogAdapter
11331 {
11332 public function __construct(\Zend_Log $logObject)
11333 {
11334 $this->log = $logObject;
11335 Version::warn(__CLASS__ . ' is deprecated');
11336 }
11337
11338 public function log($message, $priority = LOG_INFO, $extras = array())
11339 {
11340 $this->log->log($message, $priority, $extras);
11341 }
11342 }
11343 <?php
11344
11345 namespace Guzzle\Log;
11346
11347 /**
11348 * Adapter class that allows Guzzle to log data to various logging implementations.
11349 */
11350 interface LogAdapterInterface
11351 {
11352 /**
11353 * Log a message at a priority
11354 *
11355 * @param string $message Message to log
11356 * @param integer $priority Priority of message (use the \LOG_* constants of 0 - 7)
11357 * @param array $extras Extra information to log in event
11358 */
11359 public function log($message, $priority = LOG_INFO, $extras = array());
11360 }
11361 <?php
11362
11363 namespace Guzzle\Log;
11364
11365 use Zend\Log\Logger;
11366
11367 /**
11368 * Adapts a Zend Framework 2 logger object
11369 */
11370 class Zf2LogAdapter extends AbstractLogAdapter
11371 {
11372 public function __construct(Logger $logObject)
11373 {
11374 $this->log = $logObject;
11375 }
11376
11377 public function log($message, $priority = LOG_INFO, $extras = array())
11378 {
11379 $this->log->log($priority, $message, $extras);
11380 }
11381 }
11382 {
11383 "name": "guzzle/log",
11384 "description": "Guzzle log adapter component",
11385 "homepage": "http://guzzlephp.org/",
11386 "keywords": ["log", "adapter", "guzzle"],
11387 "license": "MIT",
11388 "authors": [
11389 {
11390 "name": "Michael Dowling",
11391 "email": "mtdowling@gmail.com",
11392 "homepage": "https://github.com/mtdowling"
11393 }
11394 ],
11395 "require": {
11396 "php": ">=5.3.2"
11397 },
11398 "autoload": {
11399 "psr-0": { "Guzzle\\Log": "" }
11400 },
11401 "suggest": {
11402 "guzzle/http": "self.version"
11403 },
11404 "target-dir": "Guzzle/Log",
11405 "extra": {
11406 "branch-alias": {
11407 "dev-master": "3.7-dev"
11408 }
11409 }
11410 }
11411 <?php
11412
11413 namespace Guzzle\Log;
11414
11415 use Monolog\Logger;
11416
11417 /**
11418 * @deprecated
11419 * @codeCoverageIgnore
11420 */
11421 class MonologLogAdapter extends AbstractLogAdapter
11422 {
11423 /**
11424 * syslog to Monolog mappings
11425 */
11426 private static $mapping = array(
11427 LOG_DEBUG => Logger::DEBUG,
11428 LOG_INFO => Logger::INFO,
11429 LOG_WARNING => Logger::WARNING,
11430 LOG_ERR => Logger::ERROR,
11431 LOG_CRIT => Logger::CRITICAL,
11432 LOG_ALERT => Logger::ALERT
11433 );
11434
11435 public function __construct(Logger $logObject)
11436 {
11437 $this->log = $logObject;
11438 }
11439
11440 public function log($message, $priority = LOG_INFO, $extras = array())
11441 {
11442 $this->log->addRecord(self::$mapping[$priority], $message);
11443 }
11444 }
11445 <?php
11446
11447 namespace Guzzle\Iterator;
11448
11449 /**
11450 * Proxies missing method calls to the innermost iterator
11451 */
11452 class MethodProxyIterator extends \IteratorIterator
11453 {
11454 /**
11455 * Proxy method calls to the wrapped iterator
11456 *
11457 * @param string $name Name of the method
11458 * @param array $args Arguments to proxy
11459 *
11460 * @return mixed
11461 */
11462 public function __call($name, array $args)
11463 {
11464 $i = $this->getInnerIterator();
11465 while ($i instanceof \OuterIterator) {
11466 $i = $i->getInnerIterator();
11467 }
11468
11469 return call_user_func_array(array($i, $name), $args);
11470 }
11471 }
11472 Guzzle Iterator
11473 ===============
11474
11475 Provides useful Iterators and Iterator decorators
11476
11477 - ChunkedIterator: Pulls out chunks from an inner iterator and yields the chunks as arrays
11478 - FilterIterator: Used when PHP 5.4's CallbackFilterIterator is not available
11479 - MapIterator: Maps values before yielding
11480 - MethodProxyIterator: Proxies missing method calls to the innermost iterator
11481
11482 ### Installing via Composer
11483
11484 ```bash
11485 # Install Composer
11486 curl -sS https://getcomposer.org/installer | php
11487
11488 # Add Guzzle as a dependency
11489 php composer.phar require guzzle/iterator:~3.0
11490 ```
11491
11492 After installing, you need to require Composer's autoloader:
11493
11494 ```php
11495 require 'vendor/autoload.php';
11496 ```
11497 <?php
11498
11499 namespace Guzzle\Iterator;
11500
11501 /**
11502 * Pulls out chunks from an inner iterator and yields the chunks as arrays
11503 */
11504 class ChunkedIterator extends \IteratorIterator
11505 {
11506 /** @var int Size of each chunk */
11507 protected $chunkSize;
11508
11509 /** @var array Current chunk */
11510 protected $chunk;
11511
11512 /**
11513 * @param \Traversable $iterator Traversable iterator
11514 * @param int $chunkSize Size to make each chunk
11515 * @throws \InvalidArgumentException
11516 */
11517 public function __construct(\Traversable $iterator, $chunkSize)
11518 {
11519 $chunkSize = (int) $chunkSize;
11520 if ($chunkSize < 0 ) {
11521 throw new \InvalidArgumentException("The chunk size must be equal or greater than zero; $chunkSize given");
11522 }
11523
11524 parent::__construct($iterator);
11525 $this->chunkSize = $chunkSize;
11526 }
11527
11528 public function rewind()
11529 {
11530 parent::rewind();
11531 $this->next();
11532 }
11533
11534 public function next()
11535 {
11536 $this->chunk = array();
11537 for ($i = 0; $i < $this->chunkSize && parent::valid(); $i++) {
11538 $this->chunk[] = parent::current();
11539 parent::next();
11540 }
11541 }
11542
11543 public function current()
11544 {
11545 return $this->chunk;
11546 }
11547
11548 public function valid()
11549 {
11550 return (bool) $this->chunk;
11551 }
11552 }
11553 <?php
11554
11555 namespace Guzzle\Iterator;
11556
11557 use Guzzle\Common\Exception\InvalidArgumentException;
11558
11559 /**
11560 * Maps values before yielding
11561 */
11562 class MapIterator extends \IteratorIterator
11563 {
11564 /** @var mixed Callback */
11565 protected $callback;
11566
11567 /**
11568 * @param \Traversable $iterator Traversable iterator
11569 * @param array|\Closure $callback Callback used for iterating
11570 *
11571 * @throws InvalidArgumentException if the callback if not callable
11572 */
11573 public function __construct(\Traversable $iterator, $callback)
11574 {
11575 parent::__construct($iterator);
11576 if (!is_callable($callback)) {
11577 throw new InvalidArgumentException('The callback must be callable');
11578 }
11579 $this->callback = $callback;
11580 }
11581
11582 public function current()
11583 {
11584 return call_user_func($this->callback, parent::current());
11585 }
11586 }
11587 <?php
11588
11589 namespace Guzzle\Iterator;
11590
11591 /**
11592 * AppendIterator that is not affected by https://bugs.php.net/bug.php?id=49104
11593 */
11594 class AppendIterator extends \AppendIterator
11595 {
11596 /**
11597 * Works around the bug in which PHP calls rewind() and next() when appending
11598 *
11599 * @param \Iterator $iterator Iterator to append
11600 */
11601 public function append(\Iterator $iterator)
11602 {
11603 $this->getArrayIterator()->append($iterator);
11604 }
11605 }
11606 {
11607 "name": "guzzle/iterator",
11608 "description": "Provides helpful iterators and iterator decorators",
11609 "keywords": ["iterator", "guzzle"],
11610 "homepage": "http://guzzlephp.org/",
11611 "license": "MIT",
11612 "authors": [
11613 {
11614 "name": "Michael Dowling",
11615 "email": "mtdowling@gmail.com",
11616 "homepage": "https://github.com/mtdowling"
11617 }
11618 ],
11619 "require": {
11620 "php": ">=5.3.2",
11621 "guzzle/common": ">=2.8.0"
11622 },
11623 "autoload": {
11624 "psr-0": { "Guzzle\\Iterator": "/" }
11625 },
11626 "target-dir": "Guzzle/Log",
11627 "extra": {
11628 "branch-alias": {
11629 "dev-master": "3.7-dev"
11630 }
11631 }
11632 }
11633 <?php
11634
11635 namespace Guzzle\Iterator;
11636
11637 use Guzzle\Common\Exception\InvalidArgumentException;
11638
11639 /**
11640 * Filters values using a callback
11641 *
11642 * Used when PHP 5.4's {@see \CallbackFilterIterator} is not available
11643 */
11644 class FilterIterator extends \FilterIterator
11645 {
11646 /** @var mixed Callback used for filtering */
11647 protected $callback;
11648
11649 /**
11650 * @param \Iterator $iterator Traversable iterator
11651 * @param array|\Closure $callback Callback used for filtering. Return true to keep or false to filter.
11652 *
11653 * @throws InvalidArgumentException if the callback if not callable
11654 */
11655 public function __construct(\Iterator $iterator, $callback)
11656 {
11657 parent::__construct($iterator);
11658 if (!is_callable($callback)) {
11659 throw new InvalidArgumentException('The callback must be callable');
11660 }
11661 $this->callback = $callback;
11662 }
11663
11664 public function accept()
11665 {
11666 return call_user_func($this->callback, $this->current());
11667 }
11668 }
11669 <?php
11670
11671 namespace Guzzle\Plugin\CurlAuth;
11672
11673 use Guzzle\Common\Event;
11674 use Guzzle\Common\Version;
11675 use Symfony\Component\EventDispatcher\EventSubscriberInterface;
11676
11677 /**
11678 * Adds specified curl auth to all requests sent from a client. Defaults to CURLAUTH_BASIC if none supplied.
11679 * @deprecated Use $client->getConfig()->setPath('request.options/auth', array('user', 'pass', 'Basic|Digest');
11680 */
11681 class CurlAuthPlugin implements EventSubscriberInterface
11682 {
11683 private $username;
11684 private $password;
11685 private $scheme;
11686
11687 /**
11688 * @param string $username HTTP basic auth username
11689 * @param string $password Password
11690 * @param int $scheme Curl auth scheme
11691 */
11692 public function __construct($username, $password, $scheme=CURLAUTH_BASIC)
11693 {
11694 Version::warn(__CLASS__ . " is deprecated. Use \$client->getConfig()->setPath('request.options/auth', array('user', 'pass', 'Basic|Digest');");
11695 $this->username = $username;
11696 $this->password = $password;
11697 $this->scheme = $scheme;
11698 }
11699
11700 public static function getSubscribedEvents()
11701 {
11702 return array('client.create_request' => array('onRequestCreate', 255));
11703 }
11704
11705 /**
11706 * Add basic auth
11707 *
11708 * @param Event $event
11709 */
11710 public function onRequestCreate(Event $event)
11711 {
11712 $event['request']->setAuth($this->username, $this->password, $this->scheme);
11713 }
11714 }
11715 {
11716 "name": "guzzle/plugin-curlauth",
11717 "description": "Guzzle cURL authorization plugin",
11718 "homepage": "http://guzzlephp.org/",
11719 "keywords": ["plugin", "curl", "guzzle"],
11720 "license": "MIT",
11721 "authors": [
11722 {
11723 "name": "Michael Dowling",
11724 "email": "mtdowling@gmail.com",
11725 "homepage": "https://github.com/mtdowling"
11726 }
11727 ],
11728 "require": {
11729 "php": ">=5.3.2",
11730 "guzzle/http": "self.version"
11731 },
11732 "autoload": {
11733 "psr-0": { "Guzzle\\Plugin\\CurlAuth": "" }
11734 },
11735 "target-dir": "Guzzle/Plugin/CurlAuth",
11736 "extra": {
11737 "branch-alias": {
11738 "dev-master": "3.7-dev"
11739 }
11740 }
11741 }
11742 {
11743 "name": "guzzle/plugin-async",
11744 "description": "Guzzle async request plugin",
11745 "homepage": "http://guzzlephp.org/",
11746 "keywords": ["plugin", "guzzle"],
11747 "license": "MIT",
11748 "authors": [
11749 {
11750 "name": "Michael Dowling",
11751 "email": "mtdowling@gmail.com",
11752 "homepage": "https://github.com/mtdowling"
11753 }
11754 ],
11755 "require": {
11756 "php": ">=5.3.2",
11757 "guzzle/http": "self.version"
11758 },
11759 "autoload": {
11760 "psr-0": { "Guzzle\\Plugin\\Async": "" }
11761 },
11762 "target-dir": "Guzzle/Plugin/Async",
11763 "extra": {
11764 "branch-alias": {
11765 "dev-master": "3.7-dev"
11766 }
11767 }
11768 }
11769 <?php
11770
11771 namespace Guzzle\Plugin\Async;
11772
11773 use Guzzle\Common\Event;
11774 use Guzzle\Http\Message\Response;
11775 use Guzzle\Http\Exception\CurlException;
11776 use Symfony\Component\EventDispatcher\EventSubscriberInterface;
11777
11778 /**
11779 * Sends requests but does not wait for the response
11780 */
11781 class AsyncPlugin implements EventSubscriberInterface
11782 {
11783 public static function getSubscribedEvents()
11784 {
11785 return array(
11786 'request.before_send' => 'onBeforeSend',
11787 'request.exception' => 'onRequestTimeout',
11788 'request.sent' => 'onRequestSent',
11789 'curl.callback.progress' => 'onCurlProgress'
11790 );
11791 }
11792
11793 /**
11794 * Event used to ensure that progress callback are emitted from the curl handle's request mediator.
11795 *
11796 * @param Event $event
11797 */
11798 public function onBeforeSend(Event $event)
11799 {
11800 // Ensure that progress callbacks are dispatched
11801 $event['request']->getCurlOptions()->set('progress', true);
11802 }
11803
11804 /**
11805 * Event emitted when a curl progress function is called. When the amount of data uploaded == the amount of data to
11806 * upload OR any bytes have been downloaded, then time the request out after 1ms because we're done with
11807 * transmitting the request, and tell curl not download a body.
11808 *
11809 * @param Event $event
11810 */
11811 public function onCurlProgress(Event $event)
11812 {
11813 if ($event['handle'] &&
11814 ($event['downloaded'] || ($event['uploaded'] && $event['upload_size'] === $event['uploaded']))
11815 ) {
11816 // Timeout after 1ms
11817 curl_setopt($event['handle'], CURLOPT_TIMEOUT_MS, 1);
11818 // Even if the response is quick, tell curl not to download the body
11819 curl_setopt($event['handle'], CURLOPT_NOBODY, true);
11820 }
11821 }
11822
11823 /**
11824 * Event emitted when a curl exception occurs. Ignore the exception and set a mock response.
11825 *
11826 * @param Event $event
11827 */
11828 public function onRequestTimeout(Event $event)
11829 {
11830 if ($event['exception'] instanceof CurlException) {
11831 $event['request']->setResponse(new Response(200, array(
11832 'X-Guzzle-Async' => 'Did not wait for the response'
11833 )));
11834 }
11835 }
11836
11837 /**
11838 * Event emitted when a request completes because it took less than 1ms. Add an X-Guzzle-Async header to notify the
11839 * caller that there is no body in the message.
11840 *
11841 * @param Event $event
11842 */
11843 public function onRequestSent(Event $event)
11844 {
11845 // Let the caller know this was meant to be async
11846 $event['request']->getResponse()->setHeader('X-Guzzle-Async', 'Did not wait for the response');
11847 }
11848 }
11849 <?php
11850
11851 namespace Guzzle\Plugin\Md5;
11852
11853 use Guzzle\Common\Event;
11854 use Guzzle\Http\Message\EntityEnclosingRequestInterface;
11855 use Symfony\Component\EventDispatcher\EventSubscriberInterface;
11856
11857 /**
11858 * Listener used to add a ContentMD5 header to the body of a command and adds ContentMD5 validation if the
11859 * ValidateMD5 option is not set to false on a command
11860 */
11861 class CommandContentMd5Plugin implements EventSubscriberInterface
11862 {
11863 /** @var string Parameter used to check if the ContentMD5 value is being added */
11864 protected $contentMd5Param;
11865
11866 /** @var string Parameter used to check if validation should occur on the response */
11867 protected $validateMd5Param;
11868
11869 /**
11870 * @param string $contentMd5Param Parameter used to check if the ContentMD5 value is being added
11871 * @param string $validateMd5Param Parameter used to check if validation should occur on the response
11872 */
11873 public function __construct($contentMd5Param = 'ContentMD5', $validateMd5Param = 'ValidateMD5')
11874 {
11875 $this->contentMd5Param = $contentMd5Param;
11876 $this->validateMd5Param = $validateMd5Param;
11877 }
11878
11879 public static function getSubscribedEvents()
11880 {
11881 return array('command.before_send' => array('onCommandBeforeSend', -255));
11882 }
11883
11884 public function onCommandBeforeSend(Event $event)
11885 {
11886 $command = $event['command'];
11887 $request = $command->getRequest();
11888
11889 // Only add an MD5 is there is a MD5 option on the operation and it has a payload
11890 if ($request instanceof EntityEnclosingRequestInterface && $request->getBody()
11891 && $command->getOperation()->hasParam($this->contentMd5Param)) {
11892 // Check if an MD5 checksum value should be passed along to the request
11893 if ($command[$this->contentMd5Param] === true) {
11894 if (false !== ($md5 = $request->getBody()->getContentMd5(true, true))) {
11895 $request->setHeader('Content-MD5', $md5);
11896 }
11897 }
11898 }
11899
11900 // Check if MD5 validation should be used with the response
11901 if ($command[$this->validateMd5Param] === true) {
11902 $request->addSubscriber(new Md5ValidatorPlugin(true, false));
11903 }
11904 }
11905 }
11906 <?php
11907
11908 namespace Guzzle\Plugin\Md5;
11909
11910 use Guzzle\Common\Event;
11911 use Guzzle\Common\Exception\UnexpectedValueException;
11912 use Guzzle\Http\Message\Response;
11913 use Symfony\Component\EventDispatcher\EventSubscriberInterface;
11914
11915 /**
11916 * Ensures that an the MD5 hash of an entity body matches the Content-MD5
11917 * header (if set) of an HTTP response. An exception is thrown if the
11918 * calculated MD5 does not match the expected MD5.
11919 */
11920 class Md5ValidatorPlugin implements EventSubscriberInterface
11921 {
11922 /** @var int Maximum Content-Length in bytes to validate */
11923 protected $contentLengthCutoff;
11924
11925 /** @var bool Whether or not to compare when a Content-Encoding is present */
11926 protected $contentEncoded;
11927
11928 /**
11929 * @param bool $contentEncoded Calculating the MD5 hash of an entity body where a Content-Encoding was
11930 * applied is a more expensive comparison because the entity body will need to
11931 * be compressed in order to get the correct hash. Set to FALSE to not
11932 * validate the MD5 hash of an entity body with an applied Content-Encoding.
11933 * @param bool|int $contentLengthCutoff Maximum Content-Length (bytes) in which a MD5 hash will be validated. Any
11934 * response with a Content-Length greater than this value will not be validated
11935 * because it will be deemed too memory intensive.
11936 */
11937 public function __construct($contentEncoded = true, $contentLengthCutoff = false)
11938 {
11939 $this->contentLengthCutoff = $contentLengthCutoff;
11940 $this->contentEncoded = $contentEncoded;
11941 }
11942
11943 public static function getSubscribedEvents()
11944 {
11945 return array('request.complete' => array('onRequestComplete', 255));
11946 }
11947
11948 /**
11949 * {@inheritdoc}
11950 * @throws UnexpectedValueException
11951 */
11952 public function onRequestComplete(Event $event)
11953 {
11954 $response = $event['response'];
11955
11956 if (!$contentMd5 = $response->getContentMd5()) {
11957 return;
11958 }
11959
11960 $contentEncoding = $response->getContentEncoding();
11961 if ($contentEncoding && !$this->contentEncoded) {
11962 return false;
11963 }
11964
11965 // Make sure that the size of the request is under the cutoff size
11966 if ($this->contentLengthCutoff) {
11967 $size = $response->getContentLength() ?: $response->getBody()->getSize();
11968 if (!$size || $size > $this->contentLengthCutoff) {
11969 return;
11970 }
11971 }
11972
11973 if (!$contentEncoding) {
11974 $hash = $response->getBody()->getContentMd5();
11975 } elseif ($contentEncoding == 'gzip') {
11976 $response->getBody()->compress('zlib.deflate');
11977 $hash = $response->getBody()->getContentMd5();
11978 $response->getBody()->uncompress();
11979 } elseif ($contentEncoding == 'compress') {
11980 $response->getBody()->compress('bzip2.compress');
11981 $hash = $response->getBody()->getContentMd5();
11982 $response->getBody()->uncompress();
11983 } else {
11984 return;
11985 }
11986
11987 if ($contentMd5 !== $hash) {
11988 throw new UnexpectedValueException(
11989 "The response entity body may have been modified over the wire. The Content-MD5 "
11990 . "received ({$contentMd5}) did not match the calculated MD5 hash ({$hash})."
11991 );
11992 }
11993 }
11994 }
11995 {
11996 "name": "guzzle/plugin-md5",
11997 "description": "Guzzle MD5 plugins",
11998 "homepage": "http://guzzlephp.org/",
11999 "keywords": ["plugin", "guzzle"],
12000 "license": "MIT",
12001 "authors": [
12002 {
12003 "name": "Michael Dowling",
12004 "email": "mtdowling@gmail.com",
12005 "homepage": "https://github.com/mtdowling"
12006 }
12007 ],
12008 "require": {
12009 "php": ">=5.3.2",
12010 "guzzle/http": "self.version"
12011 },
12012 "autoload": {
12013 "psr-0": { "Guzzle\\Plugin\\Md5": "" }
12014 },
12015 "target-dir": "Guzzle/Plugin/Md5",
12016 "extra": {
12017 "branch-alias": {
12018 "dev-master": "3.7-dev"
12019 }
12020 }
12021 }
12022 <?php
12023
12024 namespace Guzzle\Plugin\Cache;
12025
12026 use Guzzle\Http\Message\RequestInterface;
12027
12028 \Guzzle\Common\Version::warn('Guzzle\Plugin\Cache\DefaultCacheKeyProvider is no longer used');
12029
12030 /**
12031 * @deprecated This class is no longer used
12032 * @codeCoverageIgnore
12033 */
12034 class DefaultCacheKeyProvider implements CacheKeyProviderInterface
12035 {
12036 public function getCacheKey(RequestInterface $request)
12037 {
12038 // See if the key has already been calculated
12039 $key = $request->getParams()->get(self::CACHE_KEY);
12040
12041 if (!$key) {
12042
12043 $cloned = clone $request;
12044 $cloned->removeHeader('Cache-Control');
12045
12046 // Check to see how and if the key should be filtered
12047 foreach (explode(';', $request->getParams()->get(self::CACHE_KEY_FILTER)) as $part) {
12048 $pieces = array_map('trim', explode('=', $part));
12049 if (isset($pieces[1])) {
12050 foreach (array_map('trim', explode(',', $pieces[1])) as $remove) {
12051 if ($pieces[0] == 'header') {
12052 $cloned->removeHeader($remove);
12053 } elseif ($pieces[0] == 'query') {
12054 $cloned->getQuery()->remove($remove);
12055 }
12056 }
12057 }
12058 }
12059
12060 $raw = (string) $cloned;
12061 $key = 'GZ' . md5($raw);
12062 $request->getParams()->set(self::CACHE_KEY, $key)->set(self::CACHE_KEY_RAW, $raw);
12063 }
12064
12065 return $key;
12066 }
12067 }
12068 <?php
12069
12070 namespace Guzzle\Plugin\Cache;
12071
12072 use Guzzle\Cache\CacheAdapterFactory;
12073 use Guzzle\Cache\CacheAdapterInterface;
12074 use Guzzle\Http\EntityBodyInterface;
12075 use Guzzle\Http\Message\MessageInterface;
12076 use Guzzle\Http\Message\Request;
12077 use Guzzle\Http\Message\RequestInterface;
12078 use Guzzle\Http\Message\Response;
12079
12080 /**
12081 * Default cache storage implementation
12082 */
12083 class DefaultCacheStorage implements CacheStorageInterface
12084 {
12085 /** @var string */
12086 protected $keyPrefix;
12087
12088 /** @var CacheAdapterInterface Cache used to store cache data */
12089 protected $cache;
12090
12091 /** @var int Default cache TTL */
12092 protected $defaultTtl;
12093
12094 /**
12095 * @param mixed $cache Cache used to store cache data
12096 * @param string $keyPrefix Provide an optional key prefix to prefix on all cache keys
12097 * @param int $defaultTtl Default cache TTL
12098 */
12099 public function __construct($cache, $keyPrefix = '', $defaultTtl = 3600)
12100 {
12101 $this->cache = CacheAdapterFactory::fromCache($cache);
12102 $this->defaultTtl = $defaultTtl;
12103 $this->keyPrefix = $keyPrefix;
12104 }
12105
12106 public function cache(RequestInterface $request, Response $response)
12107 {
12108 $currentTime = time();
12109 $ttl = $request->getParams()->get('cache.override_ttl') ?: $response->getMaxAge() ?: $this->defaultTtl;
12110
12111 if ($cacheControl = $response->getHeader('Cache-Control')) {
12112 $stale = $cacheControl->getDirective('stale-if-error');
12113 $ttl += $stale == true ? $ttl : $stale;
12114 }
12115
12116 // Determine which manifest key should be used
12117 $key = $this->getCacheKey($request);
12118 $persistedRequest = $this->persistHeaders($request);
12119 $entries = array();
12120
12121 if ($manifest = $this->cache->fetch($key)) {
12122 // Determine which cache entries should still be in the cache
12123 $vary = $response->getVary();
12124 foreach (unserialize($manifest) as $entry) {
12125 // Check if the entry is expired
12126 if ($entry[4] < $currentTime) {
12127 continue;
12128 }
12129 $entry[1]['vary'] = isset($entry[1]['vary']) ? $entry[1]['vary'] : '';
12130 if ($vary != $entry[1]['vary'] || !$this->requestsMatch($vary, $entry[0], $persistedRequest)) {
12131 $entries[] = $entry;
12132 }
12133 }
12134 }
12135
12136 // Persist the response body if needed
12137 $bodyDigest = null;
12138 if ($response->getBody() && $response->getBody()->getContentLength() > 0) {
12139 $bodyDigest = $this->getBodyKey($request->getUrl(), $response->getBody());
12140 $this->cache->save($bodyDigest, (string) $response->getBody(), $ttl);
12141 }
12142
12143 array_unshift($entries, array(
12144 $persistedRequest,
12145 $this->persistHeaders($response),
12146 $response->getStatusCode(),
12147 $bodyDigest,
12148 $currentTime + $ttl
12149 ));
12150
12151 $this->cache->save($key, serialize($entries));
12152 }
12153
12154 public function delete(RequestInterface $request)
12155 {
12156 $key = $this->getCacheKey($request);
12157 if ($entries = $this->cache->fetch($key)) {
12158 // Delete each cached body
12159 foreach (unserialize($entries) as $entry) {
12160 if ($entry[3]) {
12161 $this->cache->delete($entry[3]);
12162 }
12163 }
12164 $this->cache->delete($key);
12165 }
12166 }
12167
12168 public function purge($url)
12169 {
12170 foreach (array('GET', 'HEAD', 'POST', 'PUT', 'DELETE') as $method) {
12171 $this->delete(new Request($method, $url));
12172 }
12173 }
12174
12175 public function fetch(RequestInterface $request)
12176 {
12177 $key = $this->getCacheKey($request);
12178 if (!($entries = $this->cache->fetch($key))) {
12179 return null;
12180 }
12181
12182 $match = null;
12183 $headers = $this->persistHeaders($request);
12184 $entries = unserialize($entries);
12185 foreach ($entries as $index => $entry) {
12186 if ($this->requestsMatch(isset($entry[1]['vary']) ? $entry[1]['vary'] : '', $headers, $entry[0])) {
12187 $match = $entry;
12188 break;
12189 }
12190 }
12191
12192 if (!$match) {
12193 return null;
12194 }
12195
12196 // Ensure that the response is not expired
12197 $response = null;
12198 if ($match[4] < time()) {
12199 $response = -1;
12200 } else {
12201 $response = new Response($match[2], $match[1]);
12202 if ($match[3]) {
12203 if ($body = $this->cache->fetch($match[3])) {
12204 $response->setBody($body);
12205 } else {
12206 // The response is not valid because the body was somehow deleted
12207 $response = -1;
12208 }
12209 }
12210 }
12211
12212 if ($response === -1) {
12213 // Remove the entry from the metadata and update the cache
12214 unset($entries[$index]);
12215 if ($entries) {
12216 $this->cache->save($key, serialize($entries));
12217 } else {
12218 $this->cache->delete($key);
12219 }
12220 return null;
12221 }
12222
12223 return $response;
12224 }
12225
12226 /**
12227 * Hash a request URL into a string that returns cache metadata
12228 *
12229 * @param RequestInterface $request
12230 *
12231 * @return string
12232 */
12233 protected function getCacheKey(RequestInterface $request)
12234 {
12235 // Allow cache.key_filter to trim down the URL cache key by removing generate query string values (e.g. auth)
12236 if ($filter = $request->getParams()->get('cache.key_filter')) {
12237 $url = $request->getUrl(true);
12238 foreach (explode(',', $filter) as $remove) {
12239 $url->getQuery()->remove(trim($remove));
12240 }
12241 } else {
12242 $url = $request->getUrl();
12243 }
12244
12245 return $this->keyPrefix . md5($request->getMethod() . ' ' . $url);
12246 }
12247
12248 /**
12249 * Create a cache key for a response's body
12250 *
12251 * @param string $url URL of the entry
12252 * @param EntityBodyInterface $body Response body
12253 *
12254 * @return string
12255 */
12256 protected function getBodyKey($url, EntityBodyInterface $body)
12257 {
12258 return $this->keyPrefix . md5($url) . $body->getContentMd5();
12259 }
12260
12261 /**
12262 * Determines whether two Request HTTP header sets are non-varying
12263 *
12264 * @param string $vary Response vary header
12265 * @param array $r1 HTTP header array
12266 * @param array $r2 HTTP header array
12267 *
12268 * @return bool
12269 */
12270 private function requestsMatch($vary, $r1, $r2)
12271 {
12272 if ($vary) {
12273 foreach (explode(',', $vary) as $header) {
12274 $key = trim(strtolower($header));
12275 $v1 = isset($r1[$key]) ? $r1[$key] : null;
12276 $v2 = isset($r2[$key]) ? $r2[$key] : null;
12277 if ($v1 !== $v2) {
12278 return false;
12279 }
12280 }
12281 }
12282
12283 return true;
12284 }
12285
12286 /**
12287 * Creates an array of cacheable and normalized message headers
12288 *
12289 * @param MessageInterface $message
12290 *
12291 * @return array
12292 */
12293 private function persistHeaders(MessageInterface $message)
12294 {
12295 // Headers are excluded from the caching (see RFC 2616:13.5.1)
12296 static $noCache = array(
12297 'age' => true,
12298 'connection' => true,
12299 'keep-alive' => true,
12300 'proxy-authenticate' => true,
12301 'proxy-authorization' => true,
12302 'te' => true,
12303 'trailers' => true,
12304 'transfer-encoding' => true,
12305 'upgrade' => true,
12306 'set-cookie' => true,
12307 'set-cookie2' => true
12308 );
12309
12310 // Clone the response to not destroy any necessary headers when caching
12311 $headers = $message->getHeaders()->getAll();
12312 $headers = array_diff_key($headers, $noCache);
12313 // Cast the headers to a string
12314 $headers = array_map(function ($h) { return (string) $h; }, $headers);
12315
12316 return $headers;
12317 }
12318 }
12319 <?php
12320
12321 namespace Guzzle\Plugin\Cache;
12322
12323 use Guzzle\Common\Exception\InvalidArgumentException;
12324 use Guzzle\Http\Message\RequestInterface;
12325 use Guzzle\Http\Message\Response;
12326
12327 /**
12328 * Determines if a request can be cached using a callback
12329 */
12330 class CallbackCanCacheStrategy extends DefaultCanCacheStrategy
12331 {
12332 /** @var callable Callback for request */
12333 protected $requestCallback;
12334
12335 /** @var callable Callback for response */
12336 protected $responseCallback;
12337
12338 /**
12339 * @param \Closure|array|mixed $requestCallback Callable method to invoke for requests
12340 * @param \Closure|array|mixed $responseCallback Callable method to invoke for responses
12341 *
12342 * @throws InvalidArgumentException
12343 */
12344 public function __construct($requestCallback = null, $responseCallback = null)
12345 {
12346 if ($requestCallback && !is_callable($requestCallback)) {
12347 throw new InvalidArgumentException('Method must be callable');
12348 }
12349
12350 if ($responseCallback && !is_callable($responseCallback)) {
12351 throw new InvalidArgumentException('Method must be callable');
12352 }
12353
12354 $this->requestCallback = $requestCallback;
12355 $this->responseCallback = $responseCallback;
12356 }
12357
12358 public function canCacheRequest(RequestInterface $request)
12359 {
12360 return $this->requestCallback
12361 ? call_user_func($this->requestCallback, $request)
12362 : parent::canCacheRequest($request);
12363 }
12364
12365 public function canCacheResponse(Response $response)
12366 {
12367 return $this->responseCallback
12368 ? call_user_func($this->responseCallback, $response)
12369 : parent::canCacheResponse($response);
12370 }
12371 }
12372 <?php
12373
12374 namespace Guzzle\Plugin\Cache;
12375
12376 use Guzzle\Http\Message\RequestInterface;
12377 use Guzzle\Http\Message\Response;
12378
12379 /**
12380 * Strategy used to determine if a request can be cached
12381 */
12382 interface CanCacheStrategyInterface
12383 {
12384 /**
12385 * Determine if a request can be cached
12386 *
12387 * @param RequestInterface $request Request to determine
12388 *
12389 * @return bool
12390 */
12391 public function canCacheRequest(RequestInterface $request);
12392
12393 /**
12394 * Determine if a response can be cached
12395 *
12396 * @param Response $response Response to determine
12397 *
12398 * @return bool
12399 */
12400 public function canCacheResponse(Response $response);
12401 }
12402 <?php
12403
12404 namespace Guzzle\Plugin\Cache;
12405
12406 use Guzzle\Http\Message\RequestInterface;
12407 use Guzzle\Http\Message\Response;
12408
12409 /**
12410 * Never performs cache revalidation and just assumes the request is invalid
12411 */
12412 class DenyRevalidation extends DefaultRevalidation
12413 {
12414 public function __construct() {}
12415
12416 public function revalidate(RequestInterface $request, Response $response)
12417 {
12418 return false;
12419 }
12420 }
12421 <?php
12422
12423 namespace Guzzle\Plugin\Cache;
12424
12425 use Guzzle\Cache\CacheAdapterFactory;
12426 use Guzzle\Cache\CacheAdapterInterface;
12427 use Guzzle\Common\Event;
12428 use Guzzle\Common\Exception\InvalidArgumentException;
12429 use Guzzle\Common\Version;
12430 use Guzzle\Http\Message\RequestInterface;
12431 use Guzzle\Http\Message\Response;
12432 use Guzzle\Cache\DoctrineCacheAdapter;
12433 use Guzzle\Http\Exception\CurlException;
12434 use Doctrine\Common\Cache\ArrayCache;
12435 use Symfony\Component\EventDispatcher\EventSubscriberInterface;
12436
12437 /**
12438 * Plugin to enable the caching of GET and HEAD requests. Caching can be done on all requests passing through this
12439 * plugin or only after retrieving resources with cacheable response headers.
12440 *
12441 * This is a simple implementation of RFC 2616 and should be considered a private transparent proxy cache, meaning
12442 * authorization and private data can be cached.
12443 *
12444 * It also implements RFC 5861's `stale-if-error` Cache-Control extension, allowing stale cache responses to be used
12445 * when an error is encountered (such as a `500 Internal Server Error` or DNS failure).
12446 */
12447 class CachePlugin implements EventSubscriberInterface
12448 {
12449 /** @var RevalidationInterface Cache revalidation strategy */
12450 protected $revalidation;
12451
12452 /** @var CanCacheStrategyInterface Object used to determine if a request can be cached */
12453 protected $canCache;
12454
12455 /** @var CacheStorageInterface $cache Object used to cache responses */
12456 protected $storage;
12457
12458 /** @var bool */
12459 protected $autoPurge;
12460
12461 /**
12462 * @param array|CacheAdapterInterface|CacheStorageInterface $options Array of options for the cache plugin,
12463 * cache adapter, or cache storage object.
12464 * - CacheStorageInterface storage: Adapter used to cache responses
12465 * - RevalidationInterface revalidation: Cache revalidation strategy
12466 * - CanCacheInterface can_cache: Object used to determine if a request can be cached
12467 * - bool auto_purge Set to true to automatically PURGE resources when non-idempotent
12468 * requests are sent to a resource. Defaults to false.
12469 * @throws InvalidArgumentException if no cache is provided and Doctrine cache is not installed
12470 */
12471 public function __construct($options = null)
12472 {
12473 if (!is_array($options)) {
12474 if ($options instanceof CacheAdapterInterface) {
12475 $options = array('storage' => new DefaultCacheStorage($options));
12476 } elseif ($options instanceof CacheStorageInterface) {
12477 $options = array('storage' => $options);
12478 } elseif ($options) {
12479 $options = array('storage' => new DefaultCacheStorage(CacheAdapterFactory::fromCache($options)));
12480 } elseif (!class_exists('Doctrine\Common\Cache\ArrayCache')) {
12481 // @codeCoverageIgnoreStart
12482 throw new InvalidArgumentException('No cache was provided and Doctrine is not installed');
12483 // @codeCoverageIgnoreEnd
12484 }
12485 }
12486
12487 $this->autoPurge = isset($options['auto_purge']) ? $options['auto_purge'] : false;
12488
12489 // Add a cache storage if a cache adapter was provided
12490 $this->storage = isset($options['storage'])
12491 ? $options['storage']
12492 : new DefaultCacheStorage(new DoctrineCacheAdapter(new ArrayCache()));
12493
12494 if (!isset($options['can_cache'])) {
12495 $this->canCache = new DefaultCanCacheStrategy();
12496 } else {
12497 $this->canCache = is_callable($options['can_cache'])
12498 ? new CallbackCanCacheStrategy($options['can_cache'])
12499 : $options['can_cache'];
12500 }
12501
12502 // Use the provided revalidation strategy or the default
12503 $this->revalidation = isset($options['revalidation'])
12504 ? $options['revalidation']
12505 : new DefaultRevalidation($this->storage, $this->canCache);
12506 }
12507
12508 public static function getSubscribedEvents()
12509 {
12510 return array(
12511 'request.before_send' => array('onRequestBeforeSend', -255),
12512 'request.sent' => array('onRequestSent', 255),
12513 'request.error' => array('onRequestError', 0),
12514 'request.exception' => array('onRequestException', 0),
12515 );
12516 }
12517
12518 /**
12519 * Check if a response in cache will satisfy the request before sending
12520 *
12521 * @param Event $event
12522 */
12523 public function onRequestBeforeSend(Event $event)
12524 {
12525 $request = $event['request'];
12526 $request->addHeader('Via', sprintf('%s GuzzleCache/%s', $request->getProtocolVersion(), Version::VERSION));
12527
12528 if (!$this->canCache->canCacheRequest($request)) {
12529 switch ($request->getMethod()) {
12530 case 'PURGE':
12531 $this->purge($request);
12532 $request->setResponse(new Response(200, array(), 'purged'));
12533 break;
12534 case 'PUT':
12535 case 'POST':
12536 case 'DELETE':
12537 case 'PATCH':
12538 if ($this->autoPurge) {
12539 $this->purge($request);
12540 }
12541 }
12542 return;
12543 }
12544
12545 if ($response = $this->storage->fetch($request)) {
12546 $params = $request->getParams();
12547 $params['cache.lookup'] = true;
12548 $response->setHeader(
12549 'Age',
12550 time() - strtotime($response->getDate() ? : $response->getLastModified() ?: 'now')
12551 );
12552 // Validate that the response satisfies the request
12553 if ($this->canResponseSatisfyRequest($request, $response)) {
12554 if (!isset($params['cache.hit'])) {
12555 $params['cache.hit'] = true;
12556 }
12557 $request->setResponse($response);
12558 }
12559 }
12560 }
12561
12562 /**
12563 * If possible, store a response in cache after sending
12564 *
12565 * @param Event $event
12566 */
12567 public function onRequestSent(Event $event)
12568 {
12569 $request = $event['request'];
12570 $response = $event['response'];
12571
12572 if ($request->getParams()->get('cache.hit') === null &&
12573 $this->canCache->canCacheRequest($request) &&
12574 $this->canCache->canCacheResponse($response)
12575 ) {
12576 $this->storage->cache($request, $response);
12577 }
12578
12579 $this->addResponseHeaders($request, $response);
12580 }
12581
12582 /**
12583 * If possible, return a cache response on an error
12584 *
12585 * @param Event $event
12586 */
12587 public function onRequestError(Event $event)
12588 {
12589 $request = $event['request'];
12590
12591 if (!$this->canCache->canCacheRequest($request)) {
12592 return;
12593 }
12594
12595 if ($response = $this->storage->fetch($request)) {
12596 $response->setHeader(
12597 'Age',
12598 time() - strtotime($response->getLastModified() ? : $response->getDate() ?: 'now')
12599 );
12600
12601 if ($this->canResponseSatisfyFailedRequest($request, $response)) {
12602 $request->getParams()->set('cache.hit', 'error');
12603 $this->addResponseHeaders($request, $response);
12604 $event['response'] = $response;
12605 $event->stopPropagation();
12606 }
12607 }
12608 }
12609
12610 /**
12611 * If possible, set a cache response on a cURL exception
12612 *
12613 * @param Event $event
12614 *
12615 * @return null
12616 */
12617 public function onRequestException(Event $event)
12618 {
12619 if (!$event['exception'] instanceof CurlException) {
12620 return;
12621 }
12622
12623 $request = $event['request'];
12624 if (!$this->canCache->canCacheRequest($request)) {
12625 return;
12626 }
12627
12628 if ($response = $this->storage->fetch($request)) {
12629 $response->setHeader('Age', time() - strtotime($response->getDate() ? : 'now'));
12630 if (!$this->canResponseSatisfyFailedRequest($request, $response)) {
12631 return;
12632 }
12633 $request->getParams()->set('cache.hit', 'error');
12634 $request->setResponse($response);
12635 $this->addResponseHeaders($request, $response);
12636 $event->stopPropagation();
12637 }
12638 }
12639
12640 /**
12641 * Check if a cache response satisfies a request's caching constraints
12642 *
12643 * @param RequestInterface $request Request to validate
12644 * @param Response $response Response to validate
12645 *
12646 * @return bool
12647 */
12648 public function canResponseSatisfyRequest(RequestInterface $request, Response $response)
12649 {
12650 $responseAge = $response->calculateAge();
12651 $reqc = $request->getHeader('Cache-Control');
12652 $resc = $response->getHeader('Cache-Control');
12653
12654 // Check the request's max-age header against the age of the response
12655 if ($reqc && $reqc->hasDirective('max-age') &&
12656 $responseAge > $reqc->getDirective('max-age')) {
12657 return false;
12658 }
12659
12660 // Check the response's max-age header
12661 if ($response->isFresh() === false) {
12662 $maxStale = $reqc ? $reqc->getDirective('max-stale') : null;
12663 if (null !== $maxStale) {
12664 if ($maxStale !== true && $response->getFreshness() < (-1 * $maxStale)) {
12665 return false;
12666 }
12667 } elseif ($resc && $resc->hasDirective('max-age')
12668 && $responseAge > $resc->getDirective('max-age')
12669 ) {
12670 return false;
12671 }
12672 }
12673
12674 if ($this->revalidation->shouldRevalidate($request, $response)) {
12675 try {
12676 return $this->revalidation->revalidate($request, $response);
12677 } catch (CurlException $e) {
12678 $request->getParams()->set('cache.hit', 'error');
12679 return $this->canResponseSatisfyFailedRequest($request, $response);
12680 }
12681 }
12682
12683 return true;
12684 }
12685
12686 /**
12687 * Check if a cache response satisfies a failed request's caching constraints
12688 *
12689 * @param RequestInterface $request Request to validate
12690 * @param Response $response Response to validate
12691 *
12692 * @return bool
12693 */
12694 public function canResponseSatisfyFailedRequest(RequestInterface $request, Response $response)
12695 {
12696 $reqc = $request->getHeader('Cache-Control');
12697 $resc = $response->getHeader('Cache-Control');
12698 $requestStaleIfError = $reqc ? $reqc->getDirective('stale-if-error') : null;
12699 $responseStaleIfError = $resc ? $resc->getDirective('stale-if-error') : null;
12700
12701 if (!$requestStaleIfError && !$responseStaleIfError) {
12702 return false;
12703 }
12704
12705 if (is_numeric($requestStaleIfError) && $response->getAge() - $response->getMaxAge() > $requestStaleIfError) {
12706 return false;
12707 }
12708
12709 if (is_numeric($responseStaleIfError) && $response->getAge() - $response->getMaxAge() > $responseStaleIfError) {
12710 return false;
12711 }
12712
12713 return true;
12714 }
12715
12716 /**
12717 * Purge all cache entries for a given URL
12718 *
12719 * @param string $url URL to purge
12720 */
12721 public function purge($url)
12722 {
12723 // BC compatibility with previous version that accepted a Request object
12724 $url = $url instanceof RequestInterface ? $url->getUrl() : $url;
12725 $this->storage->purge($url);
12726 }
12727
12728 /**
12729 * Add the plugin's headers to a response
12730 *
12731 * @param RequestInterface $request Request
12732 * @param Response $response Response to add headers to
12733 */
12734 protected function addResponseHeaders(RequestInterface $request, Response $response)
12735 {
12736 $params = $request->getParams();
12737 $response->setHeader('Via', sprintf('%s GuzzleCache/%s', $request->getProtocolVersion(), Version::VERSION));
12738
12739 $lookup = ($params['cache.lookup'] === true ? 'HIT' : 'MISS') . ' from GuzzleCache';
12740 if ($header = $response->getHeader('X-Cache-Lookup')) {
12741 // Don't add duplicates
12742 $values = $header->toArray();
12743 $values[] = $lookup;
12744 $response->setHeader('X-Cache-Lookup', array_unique($values));
12745 } else {
12746 $response->setHeader('X-Cache-Lookup', $lookup);
12747 }
12748
12749 if ($params['cache.hit'] === true) {
12750 $xcache = 'HIT from GuzzleCache';
12751 } elseif ($params['cache.hit'] == 'error') {
12752 $xcache = 'HIT_ERROR from GuzzleCache';
12753 } else {
12754 $xcache = 'MISS from GuzzleCache';
12755 }
12756
12757 if ($header = $response->getHeader('X-Cache')) {
12758 // Don't add duplicates
12759 $values = $header->toArray();
12760 $values[] = $xcache;
12761 $response->setHeader('X-Cache', array_unique($values));
12762 } else {
12763 $response->setHeader('X-Cache', $xcache);
12764 }
12765
12766 if ($response->isFresh() === false) {
12767 $response->addHeader('Warning', sprintf('110 GuzzleCache/%s "Response is stale"', Version::VERSION));
12768 if ($params['cache.hit'] === 'error') {
12769 $response->addHeader('Warning', sprintf('111 GuzzleCache/%s "Revalidation failed"', Version::VERSION));
12770 }
12771 }
12772 }
12773 }
12774 <?php
12775
12776 namespace Guzzle\Plugin\Cache;
12777
12778 use Guzzle\Http\Message\RequestInterface;
12779 use Guzzle\Http\Message\Response;
12780
12781 /**
12782 * Interface used to cache HTTP requests
12783 */
12784 interface CacheStorageInterface
12785 {
12786 /**
12787 * Get a Response from the cache for a request
12788 *
12789 * @param RequestInterface $request
12790 *
12791 * @return null|Response
12792 */
12793 public function fetch(RequestInterface $request);
12794
12795 /**
12796 * Cache an HTTP request
12797 *
12798 * @param RequestInterface $request Request being cached
12799 * @param Response $response Response to cache
12800 */
12801 public function cache(RequestInterface $request, Response $response);
12802
12803 /**
12804 * Deletes cache entries that match a request
12805 *
12806 * @param RequestInterface $request Request to delete from cache
12807 */
12808 public function delete(RequestInterface $request);
12809
12810 /**
12811 * Purge all cache entries for a given URL
12812 *
12813 * @param string $url
12814 */
12815 public function purge($url);
12816 }
12817 <?php
12818
12819 namespace Guzzle\Plugin\Cache;
12820
12821 \Guzzle\Common\Version::warn('Guzzle\Plugin\Cache\CacheKeyProviderInterface is no longer used');
12822
12823 /**
12824 * @deprecated This is no longer used
12825 * @codeCoverageIgnore
12826 */
12827 interface CacheKeyProviderInterface {}
12828 <?php
12829
12830 namespace Guzzle\Plugin\Cache;
12831
12832 use Guzzle\Http\Message\RequestInterface;
12833 use Guzzle\Http\Message\Response;
12834
12835 /**
12836 * Default strategy used to determine of an HTTP request can be cached
12837 */
12838 class DefaultCanCacheStrategy implements CanCacheStrategyInterface
12839 {
12840 public function canCacheRequest(RequestInterface $request)
12841 {
12842 // Only GET and HEAD requests can be cached
12843 if ($request->getMethod() != RequestInterface::GET && $request->getMethod() != RequestInterface::HEAD) {
12844 return false;
12845 }
12846
12847 // Never cache requests when using no-store
12848 if ($request->hasHeader('Cache-Control') && $request->getHeader('Cache-Control')->hasDirective('no-store')) {
12849 return false;
12850 }
12851
12852 return true;
12853 }
12854
12855 public function canCacheResponse(Response $response)
12856 {
12857 return $response->isSuccessful() && $response->canCache();
12858 }
12859 }
12860 <?php
12861
12862 namespace Guzzle\Plugin\Cache;
12863
12864 use Guzzle\Http\Message\RequestInterface;
12865 use Guzzle\Http\Message\Response;
12866 use Guzzle\Http\Exception\BadResponseException;
12867
12868 /**
12869 * Default revalidation strategy
12870 */
12871 class DefaultRevalidation implements RevalidationInterface
12872 {
12873 /** @var CacheStorageInterface Cache object storing cache data */
12874 protected $storage;
12875
12876 /** @var CanCacheStrategyInterface */
12877 protected $canCache;
12878
12879 /**
12880 * @param CacheStorageInterface $cache Cache storage
12881 * @param CanCacheStrategyInterface $canCache Determines if a message can be cached
12882 */
12883 public function __construct(CacheStorageInterface $cache, CanCacheStrategyInterface $canCache = null)
12884 {
12885 $this->storage = $cache;
12886 $this->canCache = $canCache ?: new DefaultCanCacheStrategy();
12887 }
12888
12889 public function revalidate(RequestInterface $request, Response $response)
12890 {
12891 try {
12892 $revalidate = $this->createRevalidationRequest($request, $response);
12893 $validateResponse = $revalidate->send();
12894 if ($validateResponse->getStatusCode() == 200) {
12895 return $this->handle200Response($request, $validateResponse);
12896 } elseif ($validateResponse->getStatusCode() == 304) {
12897 return $this->handle304Response($request, $validateResponse, $response);
12898 }
12899 } catch (BadResponseException $e) {
12900 $this->handleBadResponse($e);
12901 }
12902
12903 // Other exceptions encountered in the revalidation request are ignored
12904 // in hopes that sending a request to the origin server will fix it
12905 return false;
12906 }
12907
12908 public function shouldRevalidate(RequestInterface $request, Response $response)
12909 {
12910 if ($request->getMethod() != RequestInterface::GET) {
12911 return false;
12912 }
12913
12914 $reqCache = $request->getHeader('Cache-Control');
12915 $resCache = $response->getHeader('Cache-Control');
12916
12917 $revalidate = $request->getHeader('Pragma') == 'no-cache' ||
12918 ($reqCache && ($reqCache->hasDirective('no-cache') || $reqCache->hasDirective('must-revalidate'))) ||
12919 ($resCache && ($resCache->hasDirective('no-cache') || $resCache->hasDirective('must-revalidate')));
12920
12921 // Use the strong ETag validator if available and the response contains no Cache-Control directive
12922 if (!$revalidate && !$reqCache && $response->hasHeader('ETag')) {
12923 $revalidate = true;
12924 }
12925
12926 return $revalidate;
12927 }
12928
12929 /**
12930 * Handles a bad response when attempting to revalidate
12931 *
12932 * @param BadResponseException $e Exception encountered
12933 *
12934 * @throws BadResponseException
12935 */
12936 protected function handleBadResponse(BadResponseException $e)
12937 {
12938 // 404 errors mean the resource no longer exists, so remove from
12939 // cache, and prevent an additional request by throwing the exception
12940 if ($e->getResponse()->getStatusCode() == 404) {
12941 $this->storage->delete($e->getRequest());
12942 throw $e;
12943 }
12944 }
12945
12946 /**
12947 * Creates a request to use for revalidation
12948 *
12949 * @param RequestInterface $request Request
12950 * @param Response $response Response to revalidate
12951 *
12952 * @return RequestInterface returns a revalidation request
12953 */
12954 protected function createRevalidationRequest(RequestInterface $request, Response $response)
12955 {
12956 $revalidate = clone $request;
12957 $revalidate->removeHeader('Pragma')
12958 ->removeHeader('Cache-Control')
12959 ->setHeader('If-Modified-Since', $response->getLastModified() ?: $response->getDate());
12960
12961 if ($response->getEtag()) {
12962 $revalidate->setHeader('If-None-Match', $response->getEtag());
12963 }
12964
12965 // Remove any cache plugins that might be on the request to prevent infinite recursive revalidations
12966 $dispatcher = $revalidate->getEventDispatcher();
12967 foreach ($dispatcher->getListeners() as $eventName => $listeners) {
12968 foreach ($listeners as $listener) {
12969 if ($listener[0] instanceof CachePlugin) {
12970 $dispatcher->removeListener($eventName, $listener);
12971 }
12972 }
12973 }
12974
12975 return $revalidate;
12976 }
12977
12978 /**
12979 * Handles a 200 response response from revalidating. The server does not support validation, so use this response.
12980 *
12981 * @param RequestInterface $request Request that was sent
12982 * @param Response $validateResponse Response received
12983 *
12984 * @return bool Returns true if valid, false if invalid
12985 */
12986 protected function handle200Response(RequestInterface $request, Response $validateResponse)
12987 {
12988 $request->setResponse($validateResponse);
12989 if ($this->canCache->canCacheResponse($validateResponse)) {
12990 $this->storage->cache($request, $validateResponse);
12991 }
12992
12993 return false;
12994 }
12995
12996 /**
12997 * Handle a 304 response and ensure that it is still valid
12998 *
12999 * @param RequestInterface $request Request that was sent
13000 * @param Response $validateResponse Response received
13001 * @param Response $response Original cached response
13002 *
13003 * @return bool Returns true if valid, false if invalid
13004 */
13005 protected function handle304Response(RequestInterface $request, Response $validateResponse, Response $response)
13006 {
13007 static $replaceHeaders = array('Date', 'Expires', 'Cache-Control', 'ETag', 'Last-Modified');
13008
13009 // Make sure that this response has the same ETag
13010 if ($validateResponse->getEtag() != $response->getEtag()) {
13011 return false;
13012 }
13013
13014 // Replace cached headers with any of these headers from the
13015 // origin server that might be more up to date
13016 $modified = false;
13017 foreach ($replaceHeaders as $name) {
13018 if ($validateResponse->hasHeader($name)) {
13019 $modified = true;
13020 $response->setHeader($name, $validateResponse->getHeader($name));
13021 }
13022 }
13023
13024 // Store the updated response in cache
13025 if ($modified && $this->canCache->canCacheResponse($response)) {
13026 $this->storage->cache($request, $response);
13027 }
13028
13029 return true;
13030 }
13031 }
13032 <?php
13033
13034 namespace Guzzle\Plugin\Cache;
13035
13036 use Guzzle\Http\Message\RequestInterface;
13037 use Guzzle\Http\Message\Response;
13038
13039 /**
13040 * Never performs cache revalidation and just assumes the request is still ok
13041 */
13042 class SkipRevalidation extends DefaultRevalidation
13043 {
13044 public function __construct() {}
13045
13046 public function revalidate(RequestInterface $request, Response $response)
13047 {
13048 return true;
13049 }
13050 }
13051 {
13052 "name": "guzzle/plugin-cache",
13053 "description": "Guzzle HTTP cache plugin",
13054 "homepage": "http://guzzlephp.org/",
13055 "keywords": ["plugin", "guzzle"],
13056 "license": "MIT",
13057 "authors": [
13058 {
13059 "name": "Michael Dowling",
13060 "email": "mtdowling@gmail.com",
13061 "homepage": "https://github.com/mtdowling"
13062 }
13063 ],
13064 "require": {
13065 "php": ">=5.3.2",
13066 "guzzle/http": "self.version",
13067 "guzzle/cache": "self.version"
13068 },
13069 "autoload": {
13070 "psr-0": { "Guzzle\\Plugin\\Cache": "" }
13071 },
13072 "target-dir": "Guzzle/Plugin/Cache",
13073 "extra": {
13074 "branch-alias": {
13075 "dev-master": "3.7-dev"
13076 }
13077 }
13078 }
13079 <?php
13080
13081 namespace Guzzle\Plugin\Cache;
13082
13083 use Guzzle\Http\Message\RequestInterface;
13084 use Guzzle\Http\Message\Response;
13085
13086 /**
13087 * Cache revalidation interface
13088 */
13089 interface RevalidationInterface
13090 {
13091 /**
13092 * Performs a cache revalidation
13093 *
13094 * @param RequestInterface $request Request to revalidate
13095 * @param Response $response Response that was received
13096 *
13097 * @return bool Returns true if the request can be cached
13098 */
13099 public function revalidate(RequestInterface $request, Response $response);
13100
13101 /**
13102 * Returns true if the response should be revalidated
13103 *
13104 * @param RequestInterface $request Request to check
13105 * @param Response $response Response to check
13106 *
13107 * @return bool
13108 */
13109 public function shouldRevalidate(RequestInterface $request, Response $response);
13110 }
13111 <?php
13112
13113 namespace Guzzle\Plugin\Log;
13114
13115 use Guzzle\Common\Event;
13116 use Guzzle\Log\LogAdapterInterface;
13117 use Guzzle\Log\MessageFormatter;
13118 use Guzzle\Log\ClosureLogAdapter;
13119 use Guzzle\Http\EntityBody;
13120 use Guzzle\Http\Message\EntityEnclosingRequestInterface;
13121 use Guzzle\Http\Message\Response;
13122 use Symfony\Component\EventDispatcher\EventSubscriberInterface;
13123
13124 /**
13125 * Plugin class that will add request and response logging to an HTTP request.
13126 *
13127 * The log plugin uses a message formatter that allows custom messages via template variable substitution.
13128 *
13129 * @see MessageLogger for a list of available log template variable substitutions
13130 */
13131 class LogPlugin implements EventSubscriberInterface
13132 {
13133 /** @var LogAdapterInterface Adapter responsible for writing log data */
13134 protected $logAdapter;
13135
13136 /** @var MessageFormatter Formatter used to format messages before logging */
13137 protected $formatter;
13138
13139 /** @var bool Whether or not to wire request and response bodies */
13140 protected $wireBodies;
13141
13142 /**
13143 * @param LogAdapterInterface $logAdapter Adapter object used to log message
13144 * @param string|MessageFormatter $formatter Formatter used to format log messages or the formatter template
13145 * @param bool $wireBodies Set to true to track request and response bodies using a temporary
13146 * buffer if the bodies are not repeatable.
13147 */
13148 public function __construct(
13149 LogAdapterInterface $logAdapter,
13150 $formatter = null,
13151 $wireBodies = false
13152 ) {
13153 $this->logAdapter = $logAdapter;
13154 $this->formatter = $formatter instanceof MessageFormatter ? $formatter : new MessageFormatter($formatter);
13155 $this->wireBodies = $wireBodies;
13156 }
13157
13158 /**
13159 * Get a log plugin that outputs full request, response, and curl error information to stderr
13160 *
13161 * @param bool $wireBodies Set to false to disable request/response body output when they use are not repeatable
13162 * @param resource $stream Stream to write to when logging. Defaults to STDERR when it is available
13163 *
13164 * @return self
13165 */
13166 public static function getDebugPlugin($wireBodies = true, $stream = null)
13167 {
13168 if ($stream === null) {
13169 if (defined('STDERR')) {
13170 $stream = STDERR;
13171 } else {
13172 $stream = fopen('php://output', 'w');
13173 }
13174 }
13175
13176 return new self(new ClosureLogAdapter(function ($m) use ($stream) {
13177 fwrite($stream, $m . PHP_EOL);
13178 }), "# Request:\n{request}\n\n# Response:\n{response}\n\n# Errors: {curl_code} {curl_error}", $wireBodies);
13179 }
13180
13181 public static function getSubscribedEvents()
13182 {
13183 return array(
13184 'curl.callback.write' => array('onCurlWrite', 255),
13185 'curl.callback.read' => array('onCurlRead', 255),
13186 'request.before_send' => array('onRequestBeforeSend', 255),
13187 'request.sent' => array('onRequestSent', 255)
13188 );
13189 }
13190
13191 /**
13192 * Event triggered when curl data is read from a request
13193 *
13194 * @param Event $event
13195 */
13196 public function onCurlRead(Event $event)
13197 {
13198 // Stream the request body to the log if the body is not repeatable
13199 if ($wire = $event['request']->getParams()->get('request_wire')) {
13200 $wire->write($event['read']);
13201 }
13202 }
13203
13204 /**
13205 * Event triggered when curl data is written to a response
13206 *
13207 * @param Event $event
13208 */
13209 public function onCurlWrite(Event $event)
13210 {
13211 // Stream the response body to the log if the body is not repeatable
13212 if ($wire = $event['request']->getParams()->get('response_wire')) {
13213 $wire->write($event['write']);
13214 }
13215 }
13216
13217 /**
13218 * Called before a request is sent
13219 *
13220 * @param Event $event
13221 */
13222 public function onRequestBeforeSend(Event $event)
13223 {
13224 if ($this->wireBodies) {
13225 $request = $event['request'];
13226 // Ensure that curl IO events are emitted
13227 $request->getCurlOptions()->set('emit_io', true);
13228 // We need to make special handling for content wiring and non-repeatable streams.
13229 if ($request instanceof EntityEnclosingRequestInterface && $request->getBody()
13230 && (!$request->getBody()->isSeekable() || !$request->getBody()->isReadable())
13231 ) {
13232 // The body of the request cannot be recalled so logging the body will require us to buffer it
13233 $request->getParams()->set('request_wire', EntityBody::factory());
13234 }
13235 if (!$request->getResponseBody()->isRepeatable()) {
13236 // The body of the response cannot be recalled so logging the body will require us to buffer it
13237 $request->getParams()->set('response_wire', EntityBody::factory());
13238 }
13239 }
13240 }
13241
13242 /**
13243 * Triggers the actual log write when a request completes
13244 *
13245 * @param Event $event
13246 */
13247 public function onRequestSent(Event $event)
13248 {
13249 $request = $event['request'];
13250 $response = $event['response'];
13251 $handle = $event['handle'];
13252
13253 if ($wire = $request->getParams()->get('request_wire')) {
13254 $request = clone $request;
13255 $request->setBody($wire);
13256 }
13257
13258 if ($wire = $request->getParams()->get('response_wire')) {
13259 $response = clone $response;
13260 $response->setBody($wire);
13261 }
13262
13263 // Send the log message to the adapter, adding a category and host
13264 $priority = $response && $response->isError() ? LOG_ERR : LOG_DEBUG;
13265 $message = $this->formatter->format($request, $response, $handle);
13266 $this->logAdapter->log($message, $priority, array(
13267 'request' => $request,
13268 'response' => $response,
13269 'handle' => $handle
13270 ));
13271 }
13272 }
13273 {
13274 "name": "guzzle/plugin-log",
13275 "description": "Guzzle log plugin for over the wire logging",
13276 "homepage": "http://guzzlephp.org/",
13277 "keywords": ["plugin", "log", "guzzle"],
13278 "license": "MIT",
13279 "authors": [
13280 {
13281 "name": "Michael Dowling",
13282 "email": "mtdowling@gmail.com",
13283 "homepage": "https://github.com/mtdowling"
13284 }
13285 ],
13286 "require": {
13287 "php": ">=5.3.2",
13288 "guzzle/http": "self.version",
13289 "guzzle/log": "self.version"
13290 },
13291 "autoload": {
13292 "psr-0": { "Guzzle\\Plugin\\Log": "" }
13293 },
13294 "target-dir": "Guzzle/Plugin/Log",
13295 "extra": {
13296 "branch-alias": {
13297 "dev-master": "3.7-dev"
13298 }
13299 }
13300 }
13301 <?php
13302
13303 namespace Guzzle\Plugin\Backoff;
13304
13305 use Guzzle\Http\Message\RequestInterface;
13306 use Guzzle\Http\Message\Response;
13307 use Guzzle\Http\Exception\HttpException;
13308 use Guzzle\Http\Exception\CurlException;
13309
13310 /**
13311 * Strategy used to retry when certain cURL error codes are encountered.
13312 */
13313 class CurlBackoffStrategy extends AbstractErrorCodeBackoffStrategy
13314 {
13315 /** @var array Default cURL errors to retry */
13316 protected static $defaultErrorCodes = array(
13317 CURLE_COULDNT_RESOLVE_HOST, CURLE_COULDNT_CONNECT, CURLE_WRITE_ERROR, CURLE_READ_ERROR,
13318 CURLE_OPERATION_TIMEOUTED, CURLE_SSL_CONNECT_ERROR, CURLE_HTTP_PORT_FAILED, CURLE_GOT_NOTHING,
13319 CURLE_SEND_ERROR, CURLE_RECV_ERROR
13320 );
13321
13322 protected function getDelay($retries, RequestInterface $request, Response $response = null, HttpException $e = null)
13323 {
13324 if ($e && $e instanceof CurlException) {
13325 return isset($this->errorCodes[$e->getErrorNo()]) ? true : null;
13326 }
13327 }
13328 }
13329 <?php
13330
13331 namespace Guzzle\Plugin\Backoff;
13332
13333 use Guzzle\Http\Message\RequestInterface;
13334 use Guzzle\Http\Message\Response;
13335 use Guzzle\Http\Exception\HttpException;
13336
13337 /**
13338 * Implements an exponential backoff retry strategy.
13339 *
13340 * Warning: If no decision making strategies precede this strategy in the the chain, then all requests will be retried
13341 */
13342 class ExponentialBackoffStrategy extends AbstractBackoffStrategy
13343 {
13344 public function makesDecision()
13345 {
13346 return false;
13347 }
13348
13349 protected function getDelay($retries, RequestInterface $request, Response $response = null, HttpException $e = null)
13350 {
13351 return (int) pow(2, $retries);
13352 }
13353 }
13354 <?php
13355
13356 namespace Guzzle\Plugin\Backoff;
13357
13358 use Guzzle\Common\Event;
13359 use Guzzle\Log\LogAdapterInterface;
13360 use Guzzle\Log\MessageFormatter;
13361 use Symfony\Component\EventDispatcher\EventSubscriberInterface;
13362
13363 /**
13364 * Logs backoff retries triggered from the BackoffPlugin
13365 *
13366 * Format your log messages using a template that can contain template substitutions found in {@see MessageFormatter}.
13367 * In addition to the default template substitutions, there is also:
13368 *
13369 * - retries: The number of times the request has been retried
13370 * - delay: The amount of time the request is being delayed
13371 */
13372 class BackoffLogger implements EventSubscriberInterface
13373 {
13374 /** @var string Default log message template */
13375 const DEFAULT_FORMAT = '[{ts}] {method} {url} - {code} {phrase} - Retries: {retries}, Delay: {delay}, Time: {connect_time}, {total_time}, cURL: {curl_code} {curl_error}';
13376
13377 /** @var LogAdapterInterface Logger used to log retries */
13378 protected $logger;
13379
13380 /** @var MessageFormatter Formatter used to format log messages */
13381 protected $formatter;
13382
13383 /**
13384 * @param LogAdapterInterface $logger Logger used to log the retries
13385 * @param MessageFormatter $formatter Formatter used to format log messages
13386 */
13387 public function __construct(LogAdapterInterface $logger, MessageFormatter $formatter = null)
13388 {
13389 $this->logger = $logger;
13390 $this->formatter = $formatter ?: new MessageFormatter(self::DEFAULT_FORMAT);
13391 }
13392
13393 public static function getSubscribedEvents()
13394 {
13395 return array(BackoffPlugin::RETRY_EVENT => 'onRequestRetry');
13396 }
13397
13398 /**
13399 * Set the template to use for logging
13400 *
13401 * @param string $template Log message template
13402 *
13403 * @return self
13404 */
13405 public function setTemplate($template)
13406 {
13407 $this->formatter->setTemplate($template);
13408
13409 return $this;
13410 }
13411
13412 /**
13413 * Called when a request is being retried
13414 *
13415 * @param Event $event Event emitted
13416 */
13417 public function onRequestRetry(Event $event)
13418 {
13419 $this->logger->log($this->formatter->format(
13420 $event['request'],
13421 $event['response'],
13422 $event['handle'],
13423 array(
13424 'retries' => $event['retries'],
13425 'delay' => $event['delay']
13426 )
13427 ));
13428 }
13429 }
13430 <?php
13431
13432 namespace Guzzle\Plugin\Backoff;
13433
13434 use Guzzle\Http\Message\RequestInterface;
13435 use Guzzle\Http\Message\Response;
13436 use Guzzle\Http\Exception\HttpException;
13437
13438 /**
13439 * Strategy used to retry HTTP requests when the response's reason phrase matches one of the registered phrases.
13440 */
13441 class ReasonPhraseBackoffStrategy extends AbstractErrorCodeBackoffStrategy
13442 {
13443 public function makesDecision()
13444 {
13445 return true;
13446 }
13447
13448 protected function getDelay($retries, RequestInterface $request, Response $response = null, HttpException $e = null)
13449 {
13450 if ($response) {
13451 return isset($this->errorCodes[$response->getReasonPhrase()]) ? true : null;
13452 }
13453 }
13454 }
13455 <?php
13456
13457 namespace Guzzle\Plugin\Backoff;
13458
13459 use Guzzle\Common\Event;
13460 use Guzzle\Common\AbstractHasDispatcher;
13461 use Guzzle\Http\Message\EntityEnclosingRequestInterface;
13462 use Guzzle\Http\Message\RequestInterface;
13463 use Guzzle\Http\Message\Response;
13464 use Guzzle\Http\Curl\CurlMultiInterface;
13465 use Symfony\Component\EventDispatcher\EventSubscriberInterface;
13466
13467 /**
13468 * Plugin to automatically retry failed HTTP requests using a backoff strategy
13469 */
13470 class BackoffPlugin extends AbstractHasDispatcher implements EventSubscriberInterface
13471 {
13472 const DELAY_PARAM = CurlMultiInterface::BLOCKING;
13473 const RETRY_PARAM = 'plugins.backoff.retry_count';
13474 const RETRY_EVENT = 'plugins.backoff.retry';
13475
13476 /** @var BackoffStrategyInterface Backoff strategy */
13477 protected $strategy;
13478
13479 /**
13480 * @param BackoffStrategyInterface $strategy The backoff strategy used to determine whether or not to retry and
13481 * the amount of delay between retries.
13482 */
13483 public function __construct(BackoffStrategyInterface $strategy = null)
13484 {
13485 $this->strategy = $strategy;
13486 }
13487
13488 /**
13489 * Retrieve a basic truncated exponential backoff plugin that will retry HTTP errors and cURL errors
13490 *
13491 * @param int $maxRetries Maximum number of retries
13492 * @param array $httpCodes HTTP response codes to retry
13493 * @param array $curlCodes cURL error codes to retry
13494 *
13495 * @return self
13496 */
13497 public static function getExponentialBackoff(
13498 $maxRetries = 3,
13499 array $httpCodes = null,
13500 array $curlCodes = null
13501 ) {
13502 return new self(new TruncatedBackoffStrategy($maxRetries,
13503 new HttpBackoffStrategy($httpCodes,
13504 new CurlBackoffStrategy($curlCodes,
13505 new ExponentialBackoffStrategy()
13506 )
13507 )
13508 ));
13509 }
13510
13511 public static function getAllEvents()
13512 {
13513 return array(self::RETRY_EVENT);
13514 }
13515
13516 public static function getSubscribedEvents()
13517 {
13518 return array(
13519 'request.sent' => 'onRequestSent',
13520 'request.exception' => 'onRequestSent',
13521 CurlMultiInterface::POLLING_REQUEST => 'onRequestPoll'
13522 );
13523 }
13524
13525 /**
13526 * Called when a request has been sent and isn't finished processing
13527 *
13528 * @param Event $event
13529 */
13530 public function onRequestSent(Event $event)
13531 {
13532 $request = $event['request'];
13533 $response = $event['response'];
13534 $exception = $event['exception'];
13535
13536 $params = $request->getParams();
13537 $retries = (int) $params->get(self::RETRY_PARAM);
13538 $delay = $this->strategy->getBackoffPeriod($retries, $request, $response, $exception);
13539
13540 if ($delay !== false) {
13541 // Calculate how long to wait until the request should be retried
13542 $params->set(self::RETRY_PARAM, ++$retries)
13543 ->set(self::DELAY_PARAM, microtime(true) + $delay);
13544 // Send the request again
13545 $request->setState(RequestInterface::STATE_TRANSFER);
13546 $this->dispatch(self::RETRY_EVENT, array(
13547 'request' => $request,
13548 'response' => $response,
13549 'handle' => $exception ? $exception->getCurlHandle() : null,
13550 'retries' => $retries,
13551 'delay' => $delay
13552 ));
13553 }
13554 }
13555
13556 /**
13557 * Called when a request is polling in the curl multi object
13558 *
13559 * @param Event $event
13560 */
13561 public function onRequestPoll(Event $event)
13562 {
13563 $request = $event['request'];
13564 $delay = $request->getParams()->get(self::DELAY_PARAM);
13565
13566 // If the duration of the delay has passed, retry the request using the pool
13567 if (null !== $delay && microtime(true) >= $delay) {
13568 // Remove the request from the pool and then add it back again. This is required for cURL to know that we
13569 // want to retry sending the easy handle.
13570 $request->getParams()->remove(self::DELAY_PARAM);
13571 // Rewind the request body if possible
13572 if ($request instanceof EntityEnclosingRequestInterface && $request->getBody()) {
13573 $request->getBody()->seek(0);
13574 }
13575 $multi = $event['curl_multi'];
13576 $multi->remove($request);
13577 $multi->add($request);
13578 }
13579 }
13580 }
13581 <?php
13582
13583 namespace Guzzle\Plugin\Backoff;
13584
13585 use Guzzle\Http\Message\RequestInterface;
13586 use Guzzle\Http\Message\Response;
13587 use Guzzle\Http\Exception\HttpException;
13588
13589 /**
13590 * Implements a linear backoff retry strategy.
13591 *
13592 * Warning: If no decision making strategies precede this strategy in the the chain, then all requests will be retried
13593 */
13594 class LinearBackoffStrategy extends AbstractBackoffStrategy
13595 {
13596 /** @var int Amount of time to progress each delay */
13597 protected $step;
13598
13599 /**
13600 * @param int $step Amount of time to increase the delay each additional backoff
13601 */
13602 public function __construct($step = 1)
13603 {
13604 $this->step = $step;
13605 }
13606
13607 public function makesDecision()
13608 {
13609 return false;
13610 }
13611
13612 protected function getDelay($retries, RequestInterface $request, Response $response = null, HttpException $e = null)
13613 {
13614 return $retries * $this->step;
13615 }
13616 }
13617 <?php
13618
13619 namespace Guzzle\Plugin\Backoff;
13620
13621 use Guzzle\Http\Message\RequestInterface;
13622 use Guzzle\Http\Message\Response;
13623 use Guzzle\Http\Exception\HttpException;
13624
13625 /**
13626 * Strategy that will not retry more than a certain number of times.
13627 */
13628 class TruncatedBackoffStrategy extends AbstractBackoffStrategy
13629 {
13630 /** @var int Maximum number of retries per request */
13631 protected $max;
13632
13633 /**
13634 * @param int $maxRetries Maximum number of retries per request
13635 * @param BackoffStrategyInterface $next The optional next strategy
13636 */
13637 public function __construct($maxRetries, BackoffStrategyInterface $next = null)
13638 {
13639 $this->max = $maxRetries;
13640 $this->next = $next;
13641 }
13642
13643 public function makesDecision()
13644 {
13645 return true;
13646 }
13647
13648 protected function getDelay($retries, RequestInterface $request, Response $response = null, HttpException $e = null)
13649 {
13650 return $retries < $this->max ? null : false;
13651 }
13652 }
13653 <?php
13654
13655 namespace Guzzle\Plugin\Backoff;
13656
13657 use Guzzle\Http\Message\RequestInterface;
13658 use Guzzle\Http\Message\Response;
13659 use Guzzle\Http\Exception\HttpException;
13660
13661 /**
13662 * Strategy to determine if a request should be retried and how long to delay between retries
13663 */
13664 interface BackoffStrategyInterface
13665 {
13666 /**
13667 * Get the amount of time to delay in seconds before retrying a request
13668 *
13669 * @param int $retries Number of retries of the request
13670 * @param RequestInterface $request Request that was sent
13671 * @param Response $response Response that was received. Note that there may not be a response
13672 * @param HttpException $e Exception that was encountered if any
13673 *
13674 * @return bool|int Returns false to not retry or the number of seconds to delay between retries
13675 */
13676 public function getBackoffPeriod(
13677 $retries,
13678 RequestInterface $request,
13679 Response $response = null,
13680 HttpException $e = null
13681 );
13682 }
13683 <?php
13684
13685 namespace Guzzle\Plugin\Backoff;
13686
13687 use Guzzle\Http\Message\RequestInterface;
13688 use Guzzle\Http\Message\Response;
13689 use Guzzle\Http\Exception\HttpException;
13690
13691 /**
13692 * Strategy used to retry HTTP requests based on the response code.
13693 *
13694 * Retries 500 and 503 error by default.
13695 */
13696 class HttpBackoffStrategy extends AbstractErrorCodeBackoffStrategy
13697 {
13698 /** @var array Default cURL errors to retry */
13699 protected static $defaultErrorCodes = array(500, 503);
13700
13701 protected function getDelay($retries, RequestInterface $request, Response $response = null, HttpException $e = null)
13702 {
13703 if ($response) {
13704 //Short circuit the rest of the checks if it was successful
13705 if ($response->isSuccessful()) {
13706 return false;
13707 } else {
13708 return isset($this->errorCodes[$response->getStatusCode()]) ? true : null;
13709 }
13710 }
13711 }
13712 }
13713 <?php
13714
13715 namespace Guzzle\Plugin\Backoff;
13716
13717 /**
13718 * Strategy used to retry when certain error codes are encountered
13719 */
13720 abstract class AbstractErrorCodeBackoffStrategy extends AbstractBackoffStrategy
13721 {
13722 /** @var array Default cURL errors to retry */
13723 protected static $defaultErrorCodes = array();
13724
13725 /** @var array Error codes that can be retried */
13726 protected $errorCodes;
13727
13728 /**
13729 * @param array $codes Array of codes that should be retried
13730 * @param BackoffStrategyInterface $next The optional next strategy
13731 */
13732 public function __construct(array $codes = null, BackoffStrategyInterface $next = null)
13733 {
13734 $this->errorCodes = array_fill_keys($codes ?: static::$defaultErrorCodes, 1);
13735 $this->next = $next;
13736 }
13737
13738 /**
13739 * Get the default failure codes to retry
13740 *
13741 * @return array
13742 */
13743 public static function getDefaultFailureCodes()
13744 {
13745 return static::$defaultErrorCodes;
13746 }
13747
13748 public function makesDecision()
13749 {
13750 return true;
13751 }
13752 }
13753 <?php
13754
13755 namespace Guzzle\Plugin\Backoff;
13756
13757 use Guzzle\Http\Message\RequestInterface;
13758 use Guzzle\Http\Message\Response;
13759 use Guzzle\Http\Exception\HttpException;
13760
13761 /**
13762 * Will retry the request using the same amount of delay for each retry.
13763 *
13764 * Warning: If no decision making strategies precede this strategy in the the chain, then all requests will be retried
13765 */
13766 class ConstantBackoffStrategy extends AbstractBackoffStrategy
13767 {
13768 /** @var int Amount of time for each delay */
13769 protected $delay;
13770
13771 /** @param int $delay Amount of time to delay between each additional backoff */
13772 public function __construct($delay)
13773 {
13774 $this->delay = $delay;
13775 }
13776
13777 public function makesDecision()
13778 {
13779 return false;
13780 }
13781
13782 protected function getDelay($retries, RequestInterface $request, Response $response = null, HttpException $e = null)
13783 {
13784 return $this->delay;
13785 }
13786 }
13787 {
13788 "name": "guzzle/plugin-backoff",
13789 "description": "Guzzle backoff retry plugins",
13790 "homepage": "http://guzzlephp.org/",
13791 "keywords": ["plugin", "guzzle"],
13792 "license": "MIT",
13793 "authors": [
13794 {
13795 "name": "Michael Dowling",
13796 "email": "mtdowling@gmail.com",
13797 "homepage": "https://github.com/mtdowling"
13798 }
13799 ],
13800 "require": {
13801 "php": ">=5.3.2",
13802 "guzzle/http": "self.version",
13803 "guzzle/log": "self.version"
13804 },
13805 "autoload": {
13806 "psr-0": { "Guzzle\\Plugin\\Backoff": "" }
13807 },
13808 "target-dir": "Guzzle/Plugin/Backoff",
13809 "extra": {
13810 "branch-alias": {
13811 "dev-master": "3.7-dev"
13812 }
13813 }
13814 }
13815 <?php
13816
13817 namespace Guzzle\Plugin\Backoff;
13818
13819 use Guzzle\Http\Message\RequestInterface;
13820 use Guzzle\Http\Message\Response;
13821 use Guzzle\Http\Exception\HttpException;
13822
13823 /**
13824 * Abstract backoff strategy that allows for a chain of responsibility
13825 */
13826 abstract class AbstractBackoffStrategy implements BackoffStrategyInterface
13827 {
13828 /** @var AbstractBackoffStrategy Next strategy in the chain */
13829 protected $next;
13830
13831 /** @param AbstractBackoffStrategy $next Next strategy in the chain */
13832 public function setNext(AbstractBackoffStrategy $next)
13833 {
13834 $this->next = $next;
13835 }
13836
13837 /**
13838 * Get the next backoff strategy in the chain
13839 *
13840 * @return AbstractBackoffStrategy|null
13841 */
13842 public function getNext()
13843 {
13844 return $this->next;
13845 }
13846
13847 public function getBackoffPeriod(
13848 $retries,
13849 RequestInterface $request,
13850 Response $response = null,
13851 HttpException $e = null
13852 ) {
13853 $delay = $this->getDelay($retries, $request, $response, $e);
13854 if ($delay === false) {
13855 // The strategy knows that this must not be retried
13856 return false;
13857 } elseif ($delay === null) {
13858 // If the strategy is deferring a decision and the next strategy will not make a decision then return false
13859 return !$this->next || !$this->next->makesDecision()
13860 ? false
13861 : $this->next->getBackoffPeriod($retries, $request, $response, $e);
13862 } elseif ($delay === true) {
13863 // if the strategy knows that it must retry but is deferring to the next to determine the delay
13864 if (!$this->next) {
13865 return 0;
13866 } else {
13867 $next = $this->next;
13868 while ($next->makesDecision() && $next->getNext()) {
13869 $next = $next->getNext();
13870 }
13871 return !$next->makesDecision() ? $next->getBackoffPeriod($retries, $request, $response, $e) : 0;
13872 }
13873 } else {
13874 return $delay;
13875 }
13876 }
13877
13878 /**
13879 * Check if the strategy does filtering and makes decisions on whether or not to retry.
13880 *
13881 * Strategies that return false will never retry if all of the previous strategies in a chain defer on a backoff
13882 * decision.
13883 *
13884 * @return bool
13885 */
13886 abstract public function makesDecision();
13887
13888 /**
13889 * Implement the concrete strategy
13890 *
13891 * @param int $retries Number of retries of the request
13892 * @param RequestInterface $request Request that was sent
13893 * @param Response $response Response that was received. Note that there may not be a response
13894 * @param HttpException $e Exception that was encountered if any
13895 *
13896 * @return bool|int|null Returns false to not retry or the number of seconds to delay between retries. Return true
13897 * or null to defer to the next strategy if available, and if not, return 0.
13898 */
13899 abstract protected function getDelay(
13900 $retries,
13901 RequestInterface $request,
13902 Response $response = null,
13903 HttpException $e = null
13904 );
13905 }
13906 <?php
13907
13908 namespace Guzzle\Plugin\Backoff;
13909
13910 use Guzzle\Common\Exception\InvalidArgumentException;
13911 use Guzzle\Http\Message\RequestInterface;
13912 use Guzzle\Http\Message\Response;
13913 use Guzzle\Http\Exception\HttpException;
13914
13915 /**
13916 * Strategy that will invoke a closure to determine whether or not to retry with a delay
13917 */
13918 class CallbackBackoffStrategy extends AbstractBackoffStrategy
13919 {
13920 /** @var \Closure|array|mixed Callable method to invoke */
13921 protected $callback;
13922
13923 /** @var bool Whether or not this strategy makes a retry decision */
13924 protected $decision;
13925
13926 /**
13927 * @param \Closure|array|mixed $callback Callable method to invoke
13928 * @param bool $decision Set to true if this strategy makes a backoff decision
13929 * @param BackoffStrategyInterface $next The optional next strategy
13930 *
13931 * @throws InvalidArgumentException
13932 */
13933 public function __construct($callback, $decision, BackoffStrategyInterface $next = null)
13934 {
13935 if (!is_callable($callback)) {
13936 throw new InvalidArgumentException('The callback must be callable');
13937 }
13938 $this->callback = $callback;
13939 $this->decision = (bool) $decision;
13940 $this->next = $next;
13941 }
13942
13943 public function makesDecision()
13944 {
13945 return $this->decision;
13946 }
13947
13948 protected function getDelay($retries, RequestInterface $request, Response $response = null, HttpException $e = null)
13949 {
13950 return call_user_func($this->callback, $retries, $request, $response, $e);
13951 }
13952 }
13953 <?php
13954
13955 namespace Guzzle\Plugin\Oauth;
13956
13957 use Guzzle\Common\Event;
13958 use Guzzle\Common\Collection;
13959 use Guzzle\Http\Message\RequestInterface;
13960 use Guzzle\Http\Message\EntityEnclosingRequestInterface;
13961 use Guzzle\Http\QueryString;
13962 use Guzzle\Http\Url;
13963 use Symfony\Component\EventDispatcher\EventSubscriberInterface;
13964
13965 /**
13966 * OAuth signing plugin
13967 * @link http://oauth.net/core/1.0/#rfc.section.9.1.1
13968 */
13969 class OauthPlugin implements EventSubscriberInterface
13970 {
13971 /** @var Collection Configuration settings */
13972 protected $config;
13973
13974 /**
13975 * Create a new OAuth 1.0 plugin
13976 *
13977 * @param array $config Configuration array containing these parameters:
13978 * - string 'callback' OAuth callback
13979 * - string 'consumer_key' Consumer key
13980 * - string 'consumer_secret' Consumer secret
13981 * - string 'token' Token
13982 * - string 'token_secret' Token secret
13983 * - string 'verifier' OAuth verifier.
13984 * - string 'version' OAuth version. Defaults to 1.0
13985 * - string 'signature_method' Custom signature method
13986 * - bool 'disable_post_params' Set to true to prevent POST parameters from being signed
13987 * - array|Closure 'signature_callback' Custom signature callback that accepts a string to sign and a signing key
13988 */
13989 public function __construct($config)
13990 {
13991 $this->config = Collection::fromConfig($config, array(
13992 'version' => '1.0',
13993 'consumer_key' => 'anonymous',
13994 'consumer_secret' => 'anonymous',
13995 'signature_method' => 'HMAC-SHA1',
13996 'signature_callback' => function($stringToSign, $key) {
13997 return hash_hmac('sha1', $stringToSign, $key, true);
13998 }
13999 ), array(
14000 'signature_method', 'signature_callback', 'version',
14001 'consumer_key', 'consumer_secret'
14002 ));
14003 }
14004
14005 public static function getSubscribedEvents()
14006 {
14007 return array(
14008 'request.before_send' => array('onRequestBeforeSend', -1000)
14009 );
14010 }
14011
14012 /**
14013 * Request before-send event handler
14014 *
14015 * @param Event $event Event received
14016 * @return array
14017 */
14018 public function onRequestBeforeSend(Event $event)
14019 {
14020 $timestamp = $this->getTimestamp($event);
14021 $request = $event['request'];
14022 $nonce = $this->generateNonce($request);
14023 $authorizationParams = $this->getOauthParams($timestamp, $nonce);
14024 $authorizationParams['oauth_signature'] = $this->getSignature($request, $timestamp, $nonce);
14025
14026 $request->setHeader(
14027 'Authorization',
14028 $this->buildAuthorizationHeader($authorizationParams)
14029 );
14030
14031 return $authorizationParams;
14032 }
14033
14034 /**
14035 * Builds the Authorization header for a request
14036 *
14037 * @param array $authorizationParams Associative array of authorization parameters
14038 *
14039 * @return string
14040 */
14041 private function buildAuthorizationHeader($authorizationParams)
14042 {
14043 $authorizationString = 'OAuth ';
14044 foreach ($authorizationParams as $key => $val) {
14045 if ($val) {
14046 $authorizationString .= $key . '="' . urlencode($val) . '", ';
14047 }
14048 }
14049
14050 return substr($authorizationString, 0, -2);
14051 }
14052
14053 /**
14054 * Calculate signature for request
14055 *
14056 * @param RequestInterface $request Request to generate a signature for
14057 * @param integer $timestamp Timestamp to use for nonce
14058 * @param string $nonce
14059 *
14060 * @return string
14061 */
14062 public function getSignature(RequestInterface $request, $timestamp, $nonce)
14063 {
14064 $string = $this->getStringToSign($request, $timestamp, $nonce);
14065 $key = urlencode($this->config['consumer_secret']) . '&' . urlencode($this->config['token_secret']);
14066
14067 return base64_encode(call_user_func($this->config['signature_callback'], $string, $key));
14068 }
14069
14070 /**
14071 * Calculate string to sign
14072 *
14073 * @param RequestInterface $request Request to generate a signature for
14074 * @param int $timestamp Timestamp to use for nonce
14075 * @param string $nonce
14076 *
14077 * @return string
14078 */
14079 public function getStringToSign(RequestInterface $request, $timestamp, $nonce)
14080 {
14081 $params = $this->getParamsToSign($request, $timestamp, $nonce);
14082
14083 // Convert booleans to strings.
14084 $params = $this->prepareParameters($params);
14085
14086 // Build signing string from combined params
14087 $parameterString = new QueryString($params);
14088
14089 $url = Url::factory($request->getUrl())->setQuery('')->setFragment(null);
14090
14091 return strtoupper($request->getMethod()) . '&'
14092 . rawurlencode($url) . '&'
14093 . rawurlencode((string) $parameterString);
14094 }
14095
14096 /**
14097 * Get the oauth parameters as named by the oauth spec
14098 *
14099 * @param $timestamp
14100 * @param $nonce
14101 * @return Collection
14102 */
14103 protected function getOauthParams($timestamp, $nonce)
14104 {
14105 $params = new Collection(array(
14106 'oauth_consumer_key' => $this->config['consumer_key'],
14107 'oauth_nonce' => $nonce,
14108 'oauth_signature_method' => $this->config['signature_method'],
14109 'oauth_timestamp' => $timestamp,
14110 ));
14111
14112 // Optional parameters should not be set if they have not been set in the config as
14113 // the parameter may be considered invalid by the Oauth service.
14114 $optionalParams = array(
14115 'callback' => 'oauth_callback',
14116 'token' => 'oauth_token',
14117 'verifier' => 'oauth_verifier',
14118 'version' => 'oauth_version'
14119 );
14120
14121 foreach ($optionalParams as $optionName => $oauthName) {
14122 if (isset($this->config[$optionName]) == true) {
14123 $params[$oauthName] = $this->config[$optionName];
14124 }
14125 }
14126
14127 return $params;
14128 }
14129
14130 /**
14131 * Get all of the parameters required to sign a request including:
14132 * * The oauth params
14133 * * The request GET params
14134 * * The params passed in the POST body (with a content-type of application/x-www-form-urlencoded)
14135 *
14136 * @param RequestInterface $request Request to generate a signature for
14137 * @param integer $timestamp Timestamp to use for nonce
14138 * @param string $nonce
14139 *
14140 * @return array
14141 */
14142 public function getParamsToSign(RequestInterface $request, $timestamp, $nonce)
14143 {
14144 $params = $this->getOauthParams($timestamp, $nonce);
14145
14146 // Add query string parameters
14147 $params->merge($request->getQuery());
14148
14149 // Add POST fields to signing string if required
14150 if ($this->shouldPostFieldsBeSigned($request))
14151 {
14152 $params->merge($request->getPostFields());
14153 }
14154
14155 // Sort params
14156 $params = $params->toArray();
14157 ksort($params);
14158
14159 return $params;
14160 }
14161
14162 /**
14163 * Decide whether the post fields should be added to the base string that Oauth signs.
14164 * This implementation is correct. Non-conformant APIs may require that this method be
14165 * overwritten e.g. the Flickr API incorrectly adds the post fields when the Content-Type
14166 * is 'application/x-www-form-urlencoded'
14167 *
14168 * @param $request
14169 * @return bool Whether the post fields should be signed or not
14170 */
14171 public function shouldPostFieldsBeSigned($request)
14172 {
14173 if (!$this->config->get('disable_post_params') &&
14174 $request instanceof EntityEnclosingRequestInterface &&
14175 false !== strpos($request->getHeader('Content-Type'), 'application/x-www-form-urlencoded'))
14176 {
14177 return true;
14178 }
14179
14180 return false;
14181 }
14182
14183 /**
14184 * Returns a Nonce Based on the unique id and URL. This will allow for multiple requests in parallel with the same
14185 * exact timestamp to use separate nonce's.
14186 *
14187 * @param RequestInterface $request Request to generate a nonce for
14188 *
14189 * @return string
14190 */
14191 public function generateNonce(RequestInterface $request)
14192 {
14193 return sha1(uniqid('', true) . $request->getUrl());
14194 }
14195
14196 /**
14197 * Gets timestamp from event or create new timestamp
14198 *
14199 * @param Event $event Event containing contextual information
14200 *
14201 * @return int
14202 */
14203 public function getTimestamp(Event $event)
14204 {
14205 return $event['timestamp'] ?: time();
14206 }
14207
14208 /**
14209 * Convert booleans to strings, removed unset parameters, and sorts the array
14210 *
14211 * @param array $data Data array
14212 *
14213 * @return array
14214 */
14215 protected function prepareParameters($data)
14216 {
14217 ksort($data);
14218 foreach ($data as $key => &$value) {
14219 switch (gettype($value)) {
14220 case 'NULL':
14221 unset($data[$key]);
14222 break;
14223 case 'array':
14224 $data[$key] = self::prepareParameters($value);
14225 break;
14226 case 'boolean':
14227 $data[$key] = $value ? 'true' : 'false';
14228 break;
14229 }
14230 }
14231
14232 return $data;
14233 }
14234 }
14235 {
14236 "name": "guzzle/plugin-oauth",
14237 "description": "Guzzle OAuth plugin",
14238 "homepage": "http://guzzlephp.org/",
14239 "keywords": ["oauth", "plugin", "guzzle"],
14240 "license": "MIT",
14241 "authors": [
14242 {
14243 "name": "Michael Dowling",
14244 "email": "mtdowling@gmail.com",
14245 "homepage": "https://github.com/mtdowling"
14246 }
14247 ],
14248 "require": {
14249 "php": ">=5.3.2",
14250 "guzzle/http": "self.version"
14251 },
14252 "autoload": {
14253 "psr-0": { "Guzzle\\Plugin\\Oauth": "" }
14254 },
14255 "target-dir": "Guzzle/Plugin/Oauth",
14256 "extra": {
14257 "branch-alias": {
14258 "dev-master": "3.7-dev"
14259 }
14260 }
14261 }
14262 <?php
14263
14264 namespace Guzzle\Plugin\Cookie\CookieJar;
14265
14266 use Guzzle\Common\Exception\RuntimeException;
14267
14268 /**
14269 * Persists non-session cookies using a JSON formatted file
14270 */
14271 class FileCookieJar extends ArrayCookieJar
14272 {
14273 /** @var string filename */
14274 protected $filename;
14275
14276 /**
14277 * Create a new FileCookieJar object
14278 *
14279 * @param string $cookieFile File to store the cookie data
14280 *
14281 * @throws RuntimeException if the file cannot be found or created
14282 */
14283 public function __construct($cookieFile)
14284 {
14285 $this->filename = $cookieFile;
14286 $this->load();
14287 }
14288
14289 /**
14290 * Saves the file when shutting down
14291 */
14292 public function __destruct()
14293 {
14294 $this->persist();
14295 }
14296
14297 /**
14298 * Save the contents of the data array to the file
14299 *
14300 * @throws RuntimeException if the file cannot be found or created
14301 */
14302 protected function persist()
14303 {
14304 if (false === file_put_contents($this->filename, $this->serialize())) {
14305 // @codeCoverageIgnoreStart
14306 throw new RuntimeException('Unable to open file ' . $this->filename);
14307 // @codeCoverageIgnoreEnd
14308 }
14309 }
14310
14311 /**
14312 * Load the contents of the json formatted file into the data array and discard any unsaved state
14313 */
14314 protected function load()
14315 {
14316 $json = file_get_contents($this->filename);
14317 if (false === $json) {
14318 // @codeCoverageIgnoreStart
14319 throw new RuntimeException('Unable to open file ' . $this->filename);
14320 // @codeCoverageIgnoreEnd
14321 }
14322
14323 $this->unserialize($json);
14324 $this->cookies = $this->cookies ?: array();
14325 }
14326 }
14327 <?php
14328
14329 namespace Guzzle\Plugin\Cookie\CookieJar;
14330
14331 use Guzzle\Plugin\Cookie\Cookie;
14332 use Guzzle\Http\Message\RequestInterface;
14333 use Guzzle\Http\Message\Response;
14334
14335 /**
14336 * Interface for persisting cookies
14337 */
14338 interface CookieJarInterface extends \Countable, \IteratorAggregate
14339 {
14340 /**
14341 * Remove cookies currently held in the Cookie cookieJar.
14342 *
14343 * Invoking this method without arguments will empty the whole Cookie cookieJar. If given a $domain argument only
14344 * cookies belonging to that domain will be removed. If given a $domain and $path argument, cookies belonging to
14345 * the specified path within that domain are removed. If given all three arguments, then the cookie with the
14346 * specified name, path and domain is removed.
14347 *
14348 * @param string $domain Set to clear only cookies matching a domain
14349 * @param string $path Set to clear only cookies matching a domain and path
14350 * @param string $name Set to clear only cookies matching a domain, path, and name
14351 *
14352 * @return CookieJarInterface
14353 */
14354 public function remove($domain = null, $path = null, $name = null);
14355
14356 /**
14357 * Discard all temporary cookies.
14358 *
14359 * Scans for all cookies in the cookieJar with either no expire field or a true discard flag. To be called when the
14360 * user agent shuts down according to RFC 2965.
14361 *
14362 * @return CookieJarInterface
14363 */
14364 public function removeTemporary();
14365
14366 /**
14367 * Delete any expired cookies
14368 *
14369 * @return CookieJarInterface
14370 */
14371 public function removeExpired();
14372
14373 /**
14374 * Add a cookie to the cookie cookieJar
14375 *
14376 * @param Cookie $cookie Cookie to add
14377 *
14378 * @return bool Returns true on success or false on failure
14379 */
14380 public function add(Cookie $cookie);
14381
14382 /**
14383 * Add cookies from a {@see Guzzle\Http\Message\Response} object
14384 *
14385 * @param Response $response Response object
14386 * @param RequestInterface $request Request that received the response
14387 */
14388 public function addCookiesFromResponse(Response $response, RequestInterface $request = null);
14389
14390 /**
14391 * Get cookies matching a request object
14392 *
14393 * @param RequestInterface $request Request object to match
14394 *
14395 * @return array
14396 */
14397 public function getMatchingCookies(RequestInterface $request);
14398
14399 /**
14400 * Get all of the matching cookies
14401 *
14402 * @param string $domain Domain of the cookie
14403 * @param string $path Path of the cookie
14404 * @param string $name Name of the cookie
14405 * @param bool $skipDiscardable Set to TRUE to skip cookies with the Discard attribute.
14406 * @param bool $skipExpired Set to FALSE to include expired
14407 *
14408 * @return array Returns an array of Cookie objects
14409 */
14410 public function all($domain = null, $path = null, $name = null, $skipDiscardable = false, $skipExpired = true);
14411 }
14412 <?php
14413
14414 namespace Guzzle\Plugin\Cookie\CookieJar;
14415
14416 use Guzzle\Plugin\Cookie\Cookie;
14417 use Guzzle\Http\Message\RequestInterface;
14418 use Guzzle\Http\Message\Response;
14419 use Guzzle\Parser\ParserRegistry;
14420 use Guzzle\Plugin\Cookie\Exception\InvalidCookieException;
14421
14422 /**
14423 * Cookie cookieJar that stores cookies an an array
14424 */
14425 class ArrayCookieJar implements CookieJarInterface, \Serializable
14426 {
14427 /** @var array Loaded cookie data */
14428 protected $cookies = array();
14429
14430 /** @var bool Whether or not strict mode is enabled. When enabled, exceptions will be thrown for invalid cookies */
14431 protected $strictMode;
14432
14433 /**
14434 * @param bool $strictMode Set to true to throw exceptions when invalid cookies are added to the cookie jar
14435 */
14436 public function __construct($strictMode = false)
14437 {
14438 $this->strictMode = $strictMode;
14439 }
14440
14441 /**
14442 * Enable or disable strict mode on the cookie jar
14443 *
14444 * @param bool $strictMode Set to true to throw exceptions when invalid cookies are added. False to ignore them.
14445 *
14446 * @return self
14447 */
14448 public function setStrictMode($strictMode)
14449 {
14450 $this->strictMode = $strictMode;
14451 }
14452
14453 public function remove($domain = null, $path = null, $name = null)
14454 {
14455 $cookies = $this->all($domain, $path, $name, false, false);
14456 $this->cookies = array_filter($this->cookies, function (Cookie $cookie) use ($cookies) {
14457 return !in_array($cookie, $cookies, true);
14458 });
14459
14460 return $this;
14461 }
14462
14463 public function removeTemporary()
14464 {
14465 $this->cookies = array_filter($this->cookies, function (Cookie $cookie) {
14466 return !$cookie->getDiscard() && $cookie->getExpires();
14467 });
14468
14469 return $this;
14470 }
14471
14472 public function removeExpired()
14473 {
14474 $currentTime = time();
14475 $this->cookies = array_filter($this->cookies, function (Cookie $cookie) use ($currentTime) {
14476 return !$cookie->getExpires() || $currentTime < $cookie->getExpires();
14477 });
14478
14479 return $this;
14480 }
14481
14482 public function all($domain = null, $path = null, $name = null, $skipDiscardable = false, $skipExpired = true)
14483 {
14484 return array_values(array_filter($this->cookies, function (Cookie $cookie) use (
14485 $domain,
14486 $path,
14487 $name,
14488 $skipDiscardable,
14489 $skipExpired
14490 ) {
14491 return false === (($name && $cookie->getName() != $name) ||
14492 ($skipExpired && $cookie->isExpired()) ||
14493 ($skipDiscardable && ($cookie->getDiscard() || !$cookie->getExpires())) ||
14494 ($path && !$cookie->matchesPath($path)) ||
14495 ($domain && !$cookie->matchesDomain($domain)));
14496 }));
14497 }
14498
14499 public function add(Cookie $cookie)
14500 {
14501 // Only allow cookies with set and valid domain, name, value
14502 $result = $cookie->validate();
14503 if ($result !== true) {
14504 if ($this->strictMode) {
14505 throw new InvalidCookieException($result);
14506 } else {
14507 return false;
14508 }
14509 }
14510
14511 // Resolve conflicts with previously set cookies
14512 foreach ($this->cookies as $i => $c) {
14513
14514 // Two cookies are identical, when their path, domain, port and name are identical
14515 if ($c->getPath() != $cookie->getPath() ||
14516 $c->getDomain() != $cookie->getDomain() ||
14517 $c->getPorts() != $cookie->getPorts() ||
14518 $c->getName() != $cookie->getName()
14519 ) {
14520 continue;
14521 }
14522
14523 // The previously set cookie is a discard cookie and this one is not so allow the new cookie to be set
14524 if (!$cookie->getDiscard() && $c->getDiscard()) {
14525 unset($this->cookies[$i]);
14526 continue;
14527 }
14528
14529 // If the new cookie's expiration is further into the future, then replace the old cookie
14530 if ($cookie->getExpires() > $c->getExpires()) {
14531 unset($this->cookies[$i]);
14532 continue;
14533 }
14534
14535 // If the value has changed, we better change it
14536 if ($cookie->getValue() !== $c->getValue()) {
14537 unset($this->cookies[$i]);
14538 continue;
14539 }
14540
14541 // The cookie exists, so no need to continue
14542 return false;
14543 }
14544
14545 $this->cookies[] = $cookie;
14546
14547 return true;
14548 }
14549
14550 /**
14551 * Serializes the cookie cookieJar
14552 *
14553 * @return string
14554 */
14555 public function serialize()
14556 {
14557 // Only serialize long term cookies and unexpired cookies
14558 return json_encode(array_map(function (Cookie $cookie) {
14559 return $cookie->toArray();
14560 }, $this->all(null, null, null, true, true)));
14561 }
14562
14563 /**
14564 * Unserializes the cookie cookieJar
14565 */
14566 public function unserialize($data)
14567 {
14568 $data = json_decode($data, true);
14569 if (empty($data)) {
14570 $this->cookies = array();
14571 } else {
14572 $this->cookies = array_map(function (array $cookie) {
14573 return new Cookie($cookie);
14574 }, $data);
14575 }
14576 }
14577
14578 /**
14579 * Returns the total number of stored cookies
14580 *
14581 * @return int
14582 */
14583 public function count()
14584 {
14585 return count($this->cookies);
14586 }
14587
14588 /**
14589 * Returns an iterator
14590 *
14591 * @return \ArrayIterator
14592 */
14593 public function getIterator()
14594 {
14595 return new \ArrayIterator($this->cookies);
14596 }
14597
14598 public function addCookiesFromResponse(Response $response, RequestInterface $request = null)
14599 {
14600 if ($cookieHeader = $response->getHeader('Set-Cookie')) {
14601 $parser = ParserRegistry::getInstance()->getParser('cookie');
14602 foreach ($cookieHeader as $cookie) {
14603 if ($parsed = $request
14604 ? $parser->parseCookie($cookie, $request->getHost(), $request->getPath())
14605 : $parser->parseCookie($cookie)
14606 ) {
14607 // Break up cookie v2 into multiple cookies
14608 foreach ($parsed['cookies'] as $key => $value) {
14609 $row = $parsed;
14610 $row['name'] = $key;
14611 $row['value'] = $value;
14612 unset($row['cookies']);
14613 $this->add(new Cookie($row));
14614 }
14615 }
14616 }
14617 }
14618 }
14619
14620 public function getMatchingCookies(RequestInterface $request)
14621 {
14622 // Find cookies that match this request
14623 $cookies = $this->all($request->getHost(), $request->getPath());
14624 // Remove ineligible cookies
14625 foreach ($cookies as $index => $cookie) {
14626 if (!$cookie->matchesPort($request->getPort()) || ($cookie->getSecure() && $request->getScheme() != 'https')) {
14627 unset($cookies[$index]);
14628 }
14629 };
14630
14631 return $cookies;
14632 }
14633 }
14634 <?php
14635
14636 namespace Guzzle\Plugin\Cookie;
14637
14638 use Guzzle\Common\ToArrayInterface;
14639
14640 /**
14641 * Set-Cookie object
14642 */
14643 class Cookie implements ToArrayInterface
14644 {
14645 /** @var array Cookie data */
14646 protected $data;
14647
14648 /**
14649 * @var string ASCII codes not valid for for use in a cookie name
14650 *
14651 * Cookie names are defined as 'token', according to RFC 2616, Section 2.2
14652 * A valid token may contain any CHAR except CTLs (ASCII 0 - 31 or 127)
14653 * or any of the following separators
14654 */
14655 protected static $invalidCharString;
14656
14657 /**
14658 * Gets an array of invalid cookie characters
14659 *
14660 * @return array
14661 */
14662 protected static function getInvalidCharacters()
14663 {
14664 if (!self::$invalidCharString) {
14665 self::$invalidCharString = implode('', array_map('chr', array_merge(
14666 range(0, 32),
14667 array(34, 40, 41, 44, 47),
14668 array(58, 59, 60, 61, 62, 63, 64, 91, 92, 93, 123, 125, 127)
14669 )));
14670 }
14671
14672 return self::$invalidCharString;
14673 }
14674
14675 /**
14676 * @param array $data Array of cookie data provided by a Cookie parser
14677 */
14678 public function __construct(array $data = array())
14679 {
14680 static $defaults = array(
14681 'name' => '',
14682 'value' => '',
14683 'domain' => '',
14684 'path' => '/',
14685 'expires' => null,
14686 'max_age' => 0,
14687 'comment' => null,
14688 'comment_url' => null,
14689 'port' => array(),
14690 'version' => null,
14691 'secure' => false,
14692 'discard' => false,
14693 'http_only' => false
14694 );
14695
14696 $this->data = array_merge($defaults, $data);
14697 // Extract the expires value and turn it into a UNIX timestamp if needed
14698 if (!$this->getExpires() && $this->getMaxAge()) {
14699 // Calculate the expires date
14700 $this->setExpires(time() + (int) $this->getMaxAge());
14701 } elseif ($this->getExpires() && !is_numeric($this->getExpires())) {
14702 $this->setExpires(strtotime($this->getExpires()));
14703 }
14704 }
14705
14706 /**
14707 * Get the cookie as an array
14708 *
14709 * @return array
14710 */
14711 public function toArray()
14712 {
14713 return $this->data;
14714 }
14715
14716 /**
14717 * Get the cookie name
14718 *
14719 * @return string
14720 */
14721 public function getName()
14722 {
14723 return $this->data['name'];
14724 }
14725
14726 /**
14727 * Set the cookie name
14728 *
14729 * @param string $name Cookie name
14730 *
14731 * @return Cookie
14732 */
14733 public function setName($name)
14734 {
14735 return $this->setData('name', $name);
14736 }
14737
14738 /**
14739 * Get the cookie value
14740 *
14741 * @return string
14742 */
14743 public function getValue()
14744 {
14745 return $this->data['value'];
14746 }
14747
14748 /**
14749 * Set the cookie value
14750 *
14751 * @param string $value Cookie value
14752 *
14753 * @return Cookie
14754 */
14755 public function setValue($value)
14756 {
14757 return $this->setData('value', $value);
14758 }
14759
14760 /**
14761 * Get the domain
14762 *
14763 * @return string|null
14764 */
14765 public function getDomain()
14766 {
14767 return $this->data['domain'];
14768 }
14769
14770 /**
14771 * Set the domain of the cookie
14772 *
14773 * @param string $domain
14774 *
14775 * @return Cookie
14776 */
14777 public function setDomain($domain)
14778 {
14779 return $this->setData('domain', $domain);
14780 }
14781
14782 /**
14783 * Get the path
14784 *
14785 * @return string
14786 */
14787 public function getPath()
14788 {
14789 return $this->data['path'];
14790 }
14791
14792 /**
14793 * Set the path of the cookie
14794 *
14795 * @param string $path Path of the cookie
14796 *
14797 * @return Cookie
14798 */
14799 public function setPath($path)
14800 {
14801 return $this->setData('path', $path);
14802 }
14803
14804 /**
14805 * Maximum lifetime of the cookie in seconds
14806 *
14807 * @return int|null
14808 */
14809 public function getMaxAge()
14810 {
14811 return $this->data['max_age'];
14812 }
14813
14814 /**
14815 * Set the max-age of the cookie
14816 *
14817 * @param int $maxAge Max age of the cookie in seconds
14818 *
14819 * @return Cookie
14820 */
14821 public function setMaxAge($maxAge)
14822 {
14823 return $this->setData('max_age', $maxAge);
14824 }
14825
14826 /**
14827 * The UNIX timestamp when the cookie expires
14828 *
14829 * @return mixed
14830 */
14831 public function getExpires()
14832 {
14833 return $this->data['expires'];
14834 }
14835
14836 /**
14837 * Set the unix timestamp for which the cookie will expire
14838 *
14839 * @param int $timestamp Unix timestamp
14840 *
14841 * @return Cookie
14842 */
14843 public function setExpires($timestamp)
14844 {
14845 return $this->setData('expires', $timestamp);
14846 }
14847
14848 /**
14849 * Version of the cookie specification. RFC 2965 is 1
14850 *
14851 * @return mixed
14852 */
14853 public function getVersion()
14854 {
14855 return $this->data['version'];
14856 }
14857
14858 /**
14859 * Set the cookie version
14860 *
14861 * @param string|int $version Version to set
14862 *
14863 * @return Cookie
14864 */
14865 public function setVersion($version)
14866 {
14867 return $this->setData('version', $version);
14868 }
14869
14870 /**
14871 * Get whether or not this is a secure cookie
14872 *
14873 * @return null|bool
14874 */
14875 public function getSecure()
14876 {
14877 return $this->data['secure'];
14878 }
14879
14880 /**
14881 * Set whether or not the cookie is secure
14882 *
14883 * @param bool $secure Set to true or false if secure
14884 *
14885 * @return Cookie
14886 */
14887 public function setSecure($secure)
14888 {
14889 return $this->setData('secure', (bool) $secure);
14890 }
14891
14892 /**
14893 * Get whether or not this is a session cookie
14894 *
14895 * @return null|bool
14896 */
14897 public function getDiscard()
14898 {
14899 return $this->data['discard'];
14900 }
14901
14902 /**
14903 * Set whether or not this is a session cookie
14904 *
14905 * @param bool $discard Set to true or false if this is a session cookie
14906 *
14907 * @return Cookie
14908 */
14909 public function setDiscard($discard)
14910 {
14911 return $this->setData('discard', $discard);
14912 }
14913
14914 /**
14915 * Get the comment
14916 *
14917 * @return string|null
14918 */
14919 public function getComment()
14920 {
14921 return $this->data['comment'];
14922 }
14923
14924 /**
14925 * Set the comment of the cookie
14926 *
14927 * @param string $comment Cookie comment
14928 *
14929 * @return Cookie
14930 */
14931 public function setComment($comment)
14932 {
14933 return $this->setData('comment', $comment);
14934 }
14935
14936 /**
14937 * Get the comment URL of the cookie
14938 *
14939 * @return string|null
14940 */
14941 public function getCommentUrl()
14942 {
14943 return $this->data['comment_url'];
14944 }
14945
14946 /**
14947 * Set the comment URL of the cookie
14948 *
14949 * @param string $commentUrl Cookie comment URL for more information
14950 *
14951 * @return Cookie
14952 */
14953 public function setCommentUrl($commentUrl)
14954 {
14955 return $this->setData('comment_url', $commentUrl);
14956 }
14957
14958 /**
14959 * Get an array of acceptable ports this cookie can be used with
14960 *
14961 * @return array
14962 */
14963 public function getPorts()
14964 {
14965 return $this->data['port'];
14966 }
14967
14968 /**
14969 * Set a list of acceptable ports this cookie can be used with
14970 *
14971 * @param array $ports Array of acceptable ports
14972 *
14973 * @return Cookie
14974 */
14975 public function setPorts(array $ports)
14976 {
14977 return $this->setData('port', $ports);
14978 }
14979
14980 /**
14981 * Get whether or not this is an HTTP only cookie
14982 *
14983 * @return bool
14984 */
14985 public function getHttpOnly()
14986 {
14987 return $this->data['http_only'];
14988 }
14989
14990 /**
14991 * Set whether or not this is an HTTP only cookie
14992 *
14993 * @param bool $httpOnly Set to true or false if this is HTTP only
14994 *
14995 * @return Cookie
14996 */
14997 public function setHttpOnly($httpOnly)
14998 {
14999 return $this->setData('http_only', $httpOnly);
15000 }
15001
15002 /**
15003 * Get an array of extra cookie data
15004 *
15005 * @return array
15006 */
15007 public function getAttributes()
15008 {
15009 return $this->data['data'];
15010 }
15011
15012 /**
15013 * Get a specific data point from the extra cookie data
15014 *
15015 * @param string $name Name of the data point to retrieve
15016 *
15017 * @return null|string
15018 */
15019 public function getAttribute($name)
15020 {
15021 return array_key_exists($name, $this->data['data']) ? $this->data['data'][$name] : null;
15022 }
15023
15024 /**
15025 * Set a cookie data attribute
15026 *
15027 * @param string $name Name of the attribute to set
15028 * @param string $value Value to set
15029 *
15030 * @return Cookie
15031 */
15032 public function setAttribute($name, $value)
15033 {
15034 $this->data['data'][$name] = $value;
15035
15036 return $this;
15037 }
15038
15039 /**
15040 * Check if the cookie matches a path value
15041 *
15042 * @param string $path Path to check against
15043 *
15044 * @return bool
15045 */
15046 public function matchesPath($path)
15047 {
15048 return !$this->getPath() || 0 === stripos($path, $this->getPath());
15049 }
15050
15051 /**
15052 * Check if the cookie matches a domain value
15053 *
15054 * @param string $domain Domain to check against
15055 *
15056 * @return bool
15057 */
15058 public function matchesDomain($domain)
15059 {
15060 // Remove the leading '.' as per spec in RFC 6265: http://tools.ietf.org/html/rfc6265#section-5.2.3
15061 $cookieDomain = ltrim($this->getDomain(), '.');
15062
15063 // Domain not set or exact match.
15064 if (!$cookieDomain || !strcasecmp($domain, $cookieDomain)) {
15065 return true;
15066 }
15067
15068 // Matching the subdomain according to RFC 6265: http://tools.ietf.org/html/rfc6265#section-5.1.3
15069 if (filter_var($domain, FILTER_VALIDATE_IP)) {
15070 return false;
15071 }
15072
15073 return (bool) preg_match('/\.' . preg_quote($cookieDomain) . '$/i', $domain);
15074 }
15075
15076 /**
15077 * Check if the cookie is compatible with a specific port
15078 *
15079 * @param int $port Port to check
15080 *
15081 * @return bool
15082 */
15083 public function matchesPort($port)
15084 {
15085 return count($this->getPorts()) == 0 || in_array($port, $this->getPorts());
15086 }
15087
15088 /**
15089 * Check if the cookie is expired
15090 *
15091 * @return bool
15092 */
15093 public function isExpired()
15094 {
15095 return $this->getExpires() && time() > $this->getExpires();
15096 }
15097
15098 /**
15099 * Check if the cookie is valid according to RFC 6265
15100 *
15101 * @return bool|string Returns true if valid or an error message if invalid
15102 */
15103 public function validate()
15104 {
15105 // Names must not be empty, but can be 0
15106 $name = $this->getName();
15107 if (empty($name) && !is_numeric($name)) {
15108 return 'The cookie name must not be empty';
15109 }
15110
15111 // Check if any of the invalid characters are present in the cookie name
15112 if (strpbrk($name, self::getInvalidCharacters()) !== false) {
15113 return 'The cookie name must not contain invalid characters: ' . $name;
15114 }
15115
15116 // Value must not be empty, but can be 0
15117 $value = $this->getValue();
15118 if (empty($value) && !is_numeric($value)) {
15119 return 'The cookie value must not be empty';
15120 }
15121
15122 // Domains must not be empty, but can be 0
15123 // A "0" is not a valid internet domain, but may be used as server name in a private network
15124 $domain = $this->getDomain();
15125 if (empty($domain) && !is_numeric($domain)) {
15126 return 'The cookie domain must not be empty';
15127 }
15128
15129 return true;
15130 }
15131
15132 /**
15133 * Set a value and return the cookie object
15134 *
15135 * @param string $key Key to set
15136 * @param string $value Value to set
15137 *
15138 * @return Cookie
15139 */
15140 private function setData($key, $value)
15141 {
15142 $this->data[$key] = $value;
15143
15144 return $this;
15145 }
15146 }
15147 <?php
15148
15149 namespace Guzzle\Plugin\Cookie;
15150
15151 use Guzzle\Common\Event;
15152 use Guzzle\Plugin\Cookie\CookieJar\ArrayCookieJar;
15153 use Guzzle\Plugin\Cookie\CookieJar\CookieJarInterface;
15154 use Symfony\Component\EventDispatcher\EventSubscriberInterface;
15155
15156 /**
15157 * Adds, extracts, and persists cookies between HTTP requests
15158 */
15159 class CookiePlugin implements EventSubscriberInterface
15160 {
15161 /** @var CookieJarInterface Cookie cookieJar used to hold cookies */
15162 protected $cookieJar;
15163
15164 /**
15165 * @param CookieJarInterface $cookieJar Cookie jar used to hold cookies. Creates an ArrayCookieJar by default.
15166 */
15167 public function __construct(CookieJarInterface $cookieJar = null)
15168 {
15169 $this->cookieJar = $cookieJar ?: new ArrayCookieJar();
15170 }
15171
15172 public static function getSubscribedEvents()
15173 {
15174 return array(
15175 'request.before_send' => array('onRequestBeforeSend', 125),
15176 'request.sent' => array('onRequestSent', 125)
15177 );
15178 }
15179
15180 /**
15181 * Get the cookie cookieJar
15182 *
15183 * @return CookieJarInterface
15184 */
15185 public function getCookieJar()
15186 {
15187 return $this->cookieJar;
15188 }
15189
15190 /**
15191 * Add cookies before a request is sent
15192 *
15193 * @param Event $event
15194 */
15195 public function onRequestBeforeSend(Event $event)
15196 {
15197 $request = $event['request'];
15198 if (!$request->getParams()->get('cookies.disable')) {
15199 $request->removeHeader('Cookie');
15200 // Find cookies that match this request
15201 foreach ($this->cookieJar->getMatchingCookies($request) as $cookie) {
15202 $request->addCookie($cookie->getName(), $cookie->getValue());
15203 }
15204 }
15205 }
15206
15207 /**
15208 * Extract cookies from a sent request
15209 *
15210 * @param Event $event
15211 */
15212 public function onRequestSent(Event $event)
15213 {
15214 $this->cookieJar->addCookiesFromResponse($event['response'], $event['request']);
15215 }
15216 }
15217 <?php
15218
15219 namespace Guzzle\Plugin\Cookie\Exception;
15220
15221 use Guzzle\Common\Exception\InvalidArgumentException;
15222
15223 class InvalidCookieException extends InvalidArgumentException {}
15224 {
15225 "name": "guzzle/plugin-cookie",
15226 "description": "Guzzle cookie plugin",
15227 "homepage": "http://guzzlephp.org/",
15228 "keywords": ["plugin", "guzzle"],
15229 "license": "MIT",
15230 "authors": [
15231 {
15232 "name": "Michael Dowling",
15233 "email": "mtdowling@gmail.com",
15234 "homepage": "https://github.com/mtdowling"
15235 }
15236 ],
15237 "require": {
15238 "php": ">=5.3.2",
15239 "guzzle/http": "self.version"
15240 },
15241 "autoload": {
15242 "psr-0": { "Guzzle\\Plugin\\Cookie": "" }
15243 },
15244 "target-dir": "Guzzle/Plugin/Cookie",
15245 "extra": {
15246 "branch-alias": {
15247 "dev-master": "3.7-dev"
15248 }
15249 }
15250 }
15251 {
15252 "name": "guzzle/plugin",
15253 "description": "Guzzle plugin component containing all Guzzle HTTP plugins",
15254 "homepage": "http://guzzlephp.org/",
15255 "keywords": ["http", "client", "plugin", "extension", "guzzle"],
15256 "license": "MIT",
15257 "authors": [
15258 {
15259 "name": "Michael Dowling",
15260 "email": "mtdowling@gmail.com",
15261 "homepage": "https://github.com/mtdowling"
15262 }
15263 ],
15264 "require": {
15265 "php": ">=5.3.2",
15266 "guzzle/http": "self.version"
15267 },
15268 "suggest": {
15269 "guzzle/cache": "self.version",
15270 "guzzle/log": "self.version"
15271 },
15272 "autoload": {
15273 "psr-0": { "Guzzle\\Plugin": "" }
15274 },
15275 "target-dir": "Guzzle/Plugin",
15276 "replace": {
15277 "guzzle/plugin-async": "self.version",
15278 "guzzle/plugin-backoff": "self.version",
15279 "guzzle/plugin-cache": "self.version",
15280 "guzzle/plugin-cookie": "self.version",
15281 "guzzle/plugin-curlauth": "self.version",
15282 "guzzle/plugin-error-response": "self.version",
15283 "guzzle/plugin-history": "self.version",
15284 "guzzle/plugin-log": "self.version",
15285 "guzzle/plugin-md5": "self.version",
15286 "guzzle/plugin-mock": "self.version",
15287 "guzzle/plugin-oauth": "self.version"
15288 },
15289 "extra": {
15290 "branch-alias": {
15291 "dev-master": "3.7-dev"
15292 }
15293 }
15294 }
15295 <?php
15296
15297 namespace Guzzle\Plugin\Mock;
15298
15299 use Guzzle\Common\Event;
15300 use Guzzle\Common\Exception\InvalidArgumentException;
15301 use Guzzle\Common\AbstractHasDispatcher;
15302 use Guzzle\Http\Exception\CurlException;
15303 use Guzzle\Http\Message\RequestInterface;
15304 use Guzzle\Http\Message\EntityEnclosingRequestInterface;
15305 use Guzzle\Http\Message\Response;
15306 use Symfony\Component\EventDispatcher\EventSubscriberInterface;
15307
15308 /**
15309 * Queues mock responses or exceptions and delivers mock responses or exceptions in a fifo order.
15310 */
15311 class MockPlugin extends AbstractHasDispatcher implements EventSubscriberInterface, \Countable
15312 {
15313 /** @var array Array of mock responses / exceptions */
15314 protected $queue = array();
15315
15316 /** @var bool Whether or not to remove the plugin when the queue is empty */
15317 protected $temporary = false;
15318
15319 /** @var array Array of requests that were mocked */
15320 protected $received = array();
15321
15322 /** @var bool Whether or not to consume an entity body when a mock response is served */
15323 protected $readBodies;
15324
15325 /**
15326 * @param array $items Array of responses or exceptions to queue
15327 * @param bool $temporary Set to TRUE to remove the plugin when the queue is empty
15328 * @param bool $readBodies Set to TRUE to consume the entity body when a mock is served
15329 */
15330 public function __construct(array $items = null, $temporary = false, $readBodies = false)
15331 {
15332 $this->readBodies = $readBodies;
15333 $this->temporary = $temporary;
15334 if ($items) {
15335 foreach ($items as $item) {
15336 if ($item instanceof \Exception) {
15337 $this->addException($item);
15338 } else {
15339 $this->addResponse($item);
15340 }
15341 }
15342 }
15343 }
15344
15345 public static function getSubscribedEvents()
15346 {
15347 // Use a number lower than the CachePlugin
15348 return array('request.before_send' => array('onRequestBeforeSend', -999));
15349 }
15350
15351 public static function getAllEvents()
15352 {
15353 return array('mock.request');
15354 }
15355
15356 /**
15357 * Get a mock response from a file
15358 *
15359 * @param string $path File to retrieve a mock response from
15360 *
15361 * @return Response
15362 * @throws InvalidArgumentException if the file is not found
15363 */
15364 public static function getMockFile($path)
15365 {
15366 if (!file_exists($path)) {
15367 throw new InvalidArgumentException('Unable to open mock file: ' . $path);
15368 }
15369
15370 return Response::fromMessage(file_get_contents($path));
15371 }
15372
15373 /**
15374 * Set whether or not to consume the entity body of a request when a mock
15375 * response is used
15376 *
15377 * @param bool $readBodies Set to true to read and consume entity bodies
15378 *
15379 * @return self
15380 */
15381 public function readBodies($readBodies)
15382 {
15383 $this->readBodies = $readBodies;
15384
15385 return $this;
15386 }
15387
15388 /**
15389 * Returns the number of remaining mock responses
15390 *
15391 * @return int
15392 */
15393 public function count()
15394 {
15395 return count($this->queue);
15396 }
15397
15398 /**
15399 * Add a response to the end of the queue
15400 *
15401 * @param string|Response $response Response object or path to response file
15402 *
15403 * @return MockPlugin
15404 * @throws InvalidArgumentException if a string or Response is not passed
15405 */
15406 public function addResponse($response)
15407 {
15408 if (!($response instanceof Response)) {
15409 if (!is_string($response)) {
15410 throw new InvalidArgumentException('Invalid response');
15411 }
15412 $response = self::getMockFile($response);
15413 }
15414
15415 $this->queue[] = $response;
15416
15417 return $this;
15418 }
15419
15420 /**
15421 * Add an exception to the end of the queue
15422 *
15423 * @param CurlException $e Exception to throw when the request is executed
15424 *
15425 * @return MockPlugin
15426 */
15427 public function addException(CurlException $e)
15428 {
15429 $this->queue[] = $e;
15430
15431 return $this;
15432 }
15433
15434 /**
15435 * Clear the queue
15436 *
15437 * @return MockPlugin
15438 */
15439 public function clearQueue()
15440 {
15441 $this->queue = array();
15442
15443 return $this;
15444 }
15445
15446 /**
15447 * Returns an array of mock responses remaining in the queue
15448 *
15449 * @return array
15450 */
15451 public function getQueue()
15452 {
15453 return $this->queue;
15454 }
15455
15456 /**
15457 * Check if this is a temporary plugin
15458 *
15459 * @return bool
15460 */
15461 public function isTemporary()
15462 {
15463 return $this->temporary;
15464 }
15465
15466 /**
15467 * Get a response from the front of the list and add it to a request
15468 *
15469 * @param RequestInterface $request Request to mock
15470 *
15471 * @return self
15472 * @throws CurlException When request.send is called and an exception is queued
15473 */
15474 public function dequeue(RequestInterface $request)
15475 {
15476 $this->dispatch('mock.request', array('plugin' => $this, 'request' => $request));
15477
15478 $item = array_shift($this->queue);
15479 if ($item instanceof Response) {
15480 if ($this->readBodies && $request instanceof EntityEnclosingRequestInterface) {
15481 $request->getEventDispatcher()->addListener('request.sent', $f = function (Event $event) use (&$f) {
15482 while ($data = $event['request']->getBody()->read(8096));
15483 // Remove the listener after one-time use
15484 $event['request']->getEventDispatcher()->removeListener('request.sent', $f);
15485 });
15486 }
15487 $request->setResponse($item);
15488 } elseif ($item instanceof CurlException) {
15489 // Emulates exceptions encountered while transferring requests
15490 $item->setRequest($request);
15491 $state = $request->setState(RequestInterface::STATE_ERROR, array('exception' => $item));
15492 // Only throw if the exception wasn't handled
15493 if ($state == RequestInterface::STATE_ERROR) {
15494 throw $item;
15495 }
15496 }
15497
15498 return $this;
15499 }
15500
15501 /**
15502 * Clear the array of received requests
15503 */
15504 public function flush()
15505 {
15506 $this->received = array();
15507 }
15508
15509 /**
15510 * Get an array of requests that were mocked by this plugin
15511 *
15512 * @return array
15513 */
15514 public function getReceivedRequests()
15515 {
15516 return $this->received;
15517 }
15518
15519 /**
15520 * Called when a request is about to be sent
15521 *
15522 * @param Event $event
15523 */
15524 public function onRequestBeforeSend(Event $event)
15525 {
15526 if ($this->queue) {
15527 $request = $event['request'];
15528 $this->received[] = $request;
15529 // Detach the filter from the client so it's a one-time use
15530 if ($this->temporary && count($this->queue) == 1 && $request->getClient()) {
15531 $request->getClient()->getEventDispatcher()->removeSubscriber($this);
15532 }
15533 $this->dequeue($request);
15534 }
15535 }
15536 }
15537 {
15538 "name": "guzzle/plugin-mock",
15539 "description": "Guzzle Mock plugin",
15540 "homepage": "http://guzzlephp.org/",
15541 "keywords": ["mock", "plugin", "guzzle"],
15542 "license": "MIT",
15543 "authors": [
15544 {
15545 "name": "Michael Dowling",
15546 "email": "mtdowling@gmail.com",
15547 "homepage": "https://github.com/mtdowling"
15548 }
15549 ],
15550 "require": {
15551 "php": ">=5.3.2",
15552 "guzzle/http": "self.version"
15553 },
15554 "autoload": {
15555 "psr-0": { "Guzzle\\Plugin\\Mock": "" }
15556 },
15557 "target-dir": "Guzzle/Plugin/Mock",
15558 "extra": {
15559 "branch-alias": {
15560 "dev-master": "3.7-dev"
15561 }
15562 }
15563 }
15564 <?php
15565
15566 namespace Guzzle\Plugin\ErrorResponse;
15567
15568 use Guzzle\Common\Event;
15569 use Guzzle\Http\Message\RequestInterface;
15570 use Guzzle\Service\Command\CommandInterface;
15571 use Guzzle\Service\Description\Operation;
15572 use Guzzle\Plugin\ErrorResponse\Exception\ErrorResponseException;
15573 use Symfony\Component\EventDispatcher\EventSubscriberInterface;
15574
15575 /**
15576 * Converts generic Guzzle response exceptions into errorResponse exceptions
15577 */
15578 class ErrorResponsePlugin implements EventSubscriberInterface
15579 {
15580 public static function getSubscribedEvents()
15581 {
15582 return array('command.before_send' => array('onCommandBeforeSend', -1));
15583 }
15584
15585 /**
15586 * Adds a listener to requests before they sent from a command
15587 *
15588 * @param Event $event Event emitted
15589 */
15590 public function onCommandBeforeSend(Event $event)
15591 {
15592 $command = $event['command'];
15593 if ($operation = $command->getOperation()) {
15594 if ($operation->getErrorResponses()) {
15595 $request = $command->getRequest();
15596 $request->getEventDispatcher()
15597 ->addListener('request.complete', $this->getErrorClosure($request, $command, $operation));
15598 }
15599 }
15600 }
15601
15602 /**
15603 * @param RequestInterface $request Request that received an error
15604 * @param CommandInterface $command Command that created the request
15605 * @param Operation $operation Operation that defines the request and errors
15606 *
15607 * @return \Closure Returns a closure
15608 * @throws ErrorResponseException
15609 */
15610 protected function getErrorClosure(RequestInterface $request, CommandInterface $command, Operation $operation)
15611 {
15612 return function (Event $event) use ($request, $command, $operation) {
15613 $response = $event['response'];
15614 foreach ($operation->getErrorResponses() as $error) {
15615 if (!isset($error['class'])) {
15616 continue;
15617 }
15618 if (isset($error['code']) && $response->getStatusCode() != $error['code']) {
15619 continue;
15620 }
15621 if (isset($error['reason']) && $response->getReasonPhrase() != $error['reason']) {
15622 continue;
15623 }
15624 $className = $error['class'];
15625 $errorClassInterface = __NAMESPACE__ . '\\ErrorResponseExceptionInterface';
15626 if (!class_exists($className)) {
15627 throw new ErrorResponseException("{$className} does not exist");;
15628 } elseif (!is_subclass_of($className, $errorClassInterface)) {
15629 throw new ErrorResponseException("{$className} must implement {$errorClassInterface}");
15630 }
15631 throw $className::fromCommand($command, $response);
15632 }
15633 };
15634 }
15635 }
15636 <?php
15637
15638 namespace Guzzle\Plugin\ErrorResponse;
15639
15640 use Guzzle\Service\Command\CommandInterface;
15641 use Guzzle\Http\Message\Response;
15642
15643 /**
15644 * Interface used to create an exception from an error response
15645 */
15646 interface ErrorResponseExceptionInterface
15647 {
15648 /**
15649 * Create an exception for a command based on a command and an error response definition
15650 *
15651 * @param CommandInterface $command Command that was sent
15652 * @param Response $response The error response
15653 *
15654 * @return self
15655 */
15656 public static function fromCommand(CommandInterface $command, Response $response);
15657 }
15658 <?php
15659
15660 namespace Guzzle\Plugin\ErrorResponse\Exception;
15661
15662 use Guzzle\Common\Exception\RuntimeException;
15663
15664 class ErrorResponseException extends RuntimeException {}
15665 {
15666 "name": "guzzle/plugin-error-response",
15667 "description": "Guzzle errorResponse plugin for creating error exceptions based on a service description",
15668 "homepage": "http://guzzlephp.org/",
15669 "keywords": ["plugin", "guzzle"],
15670 "license": "MIT",
15671 "authors": [
15672 {
15673 "name": "Michael Dowling",
15674 "email": "mtdowling@gmail.com",
15675 "homepage": "https://github.com/mtdowling"
15676 }
15677 ],
15678 "require": {
15679 "php": ">=5.3.2",
15680 "guzzle/service": "self.version"
15681 },
15682 "autoload": {
15683 "psr-0": { "Guzzle\\Plugin\\ErrorResponse": "" }
15684 },
15685 "target-dir": "Guzzle/Plugin/ErrorResponse",
15686 "extra": {
15687 "branch-alias": {
15688 "dev-master": "3.7-dev"
15689 }
15690 }
15691 }
15692 <?php
15693
15694 namespace Guzzle\Plugin\History;
15695
15696 use Guzzle\Common\Event;
15697 use Guzzle\Http\Message\RequestInterface;
15698 use Guzzle\Http\Message\Response;
15699 use Symfony\Component\EventDispatcher\EventSubscriberInterface;
15700
15701 /**
15702 * Maintains a list of requests and responses sent using a request or client
15703 */
15704 class HistoryPlugin implements EventSubscriberInterface, \IteratorAggregate, \Countable
15705 {
15706 /** @var int The maximum number of requests to maintain in the history */
15707 protected $limit = 10;
15708
15709 /** @var array Requests and responses that have passed through the plugin */
15710 protected $transactions = array();
15711
15712 public static function getSubscribedEvents()
15713 {
15714 return array('request.sent' => array('onRequestSent', 9999));
15715 }
15716
15717 /**
15718 * Convert to a string that contains all request and response headers
15719 *
15720 * @return string
15721 */
15722 public function __toString()
15723 {
15724 $lines = array();
15725 foreach ($this->transactions as $entry) {
15726 $response = isset($entry['response']) ? $entry['response'] : '';
15727 $lines[] = '> ' . trim($entry['request']) . "\n\n< " . trim($response) . "\n";
15728 }
15729
15730 return implode("\n", $lines);
15731 }
15732
15733 /**
15734 * Add a request to the history
15735 *
15736 * @param RequestInterface $request Request to add
15737 * @param Response $response Response of the request
15738 *
15739 * @return HistoryPlugin
15740 */
15741 public function add(RequestInterface $request, Response $response = null)
15742 {
15743 if (!$response && $request->getResponse()) {
15744 $response = $request->getResponse();
15745 }
15746
15747 $this->transactions[] = array('request' => $request, 'response' => $response);
15748 if (count($this->transactions) > $this->getlimit()) {
15749 array_shift($this->transactions);
15750 }
15751
15752 return $this;
15753 }
15754
15755 /**
15756 * Set the max number of requests to store
15757 *
15758 * @param int $limit Limit
15759 *
15760 * @return HistoryPlugin
15761 */
15762 public function setLimit($limit)
15763 {
15764 $this->limit = (int) $limit;
15765
15766 return $this;
15767 }
15768
15769 /**
15770 * Get the request limit
15771 *
15772 * @return int
15773 */
15774 public function getLimit()
15775 {
15776 return $this->limit;
15777 }
15778
15779 /**
15780 * Get all of the raw transactions in the form of an array of associative arrays containing
15781 * 'request' and 'response' keys.
15782 *
15783 * @return array
15784 */
15785 public function getAll()
15786 {
15787 return $this->transactions;
15788 }
15789
15790 /**
15791 * Get the requests in the history
15792 *
15793 * @return \ArrayIterator
15794 */
15795 public function getIterator()
15796 {
15797 // Return an iterator just like the old iteration of the HistoryPlugin for BC compatibility (use getAll())
15798 return new \ArrayIterator(array_map(function ($entry) {
15799 $entry['request']->getParams()->set('actual_response', $entry['response']);
15800 return $entry['request'];
15801 }, $this->transactions));
15802 }
15803
15804 /**
15805 * Get the number of requests in the history
15806 *
15807 * @return int
15808 */
15809 public function count()
15810 {
15811 return count($this->transactions);
15812 }
15813
15814 /**
15815 * Get the last request sent
15816 *
15817 * @return RequestInterface
15818 */
15819 public function getLastRequest()
15820 {
15821 $last = end($this->transactions);
15822
15823 return $last['request'];
15824 }
15825
15826 /**
15827 * Get the last response in the history
15828 *
15829 * @return Response|null
15830 */
15831 public function getLastResponse()
15832 {
15833 $last = end($this->transactions);
15834
15835 return isset($last['response']) ? $last['response'] : null;
15836 }
15837
15838 /**
15839 * Clears the history
15840 *
15841 * @return HistoryPlugin
15842 */
15843 public function clear()
15844 {
15845 $this->transactions = array();
15846
15847 return $this;
15848 }
15849
15850 public function onRequestSent(Event $event)
15851 {
15852 $this->add($event['request'], $event['response']);
15853 }
15854 }
15855 {
15856 "name": "guzzle/plugin-history",
15857 "description": "Guzzle history plugin",
15858 "homepage": "http://guzzlephp.org/",
15859 "keywords": ["plugin", "guzzle"],
15860 "license": "MIT",
15861 "authors": [
15862 {
15863 "name": "Michael Dowling",
15864 "email": "mtdowling@gmail.com",
15865 "homepage": "https://github.com/mtdowling"
15866 }
15867 ],
15868 "require": {
15869 "php": ">=5.3.2",
15870 "guzzle/http": "self.version"
15871 },
15872 "autoload": {
15873 "psr-0": { "Guzzle\\Plugin\\History": "" }
15874 },
15875 "target-dir": "Guzzle/Plugin/History",
15876 "extra": {
15877 "branch-alias": {
15878 "dev-master": "3.7-dev"
15879 }
15880 }
15881 }
15882 <?php
15883
15884 namespace Guzzle\Common;
15885
15886 /**
15887 * Guzzle version information
15888 */
15889 class Version
15890 {
15891 const VERSION = '3.7.3';
15892
15893 /**
15894 * @var bool Set this value to true to enable warnings for deprecated functionality use. This should be on in your
15895 * unit tests, but probably not in production.
15896 */
15897 public static $emitWarnings = false;
15898
15899 /**
15900 * Emit a deprecation warning
15901 *
15902 * @param string $message Warning message
15903 */
15904 public static function warn($message)
15905 {
15906 if (self::$emitWarnings) {
15907 trigger_error('Deprecation warning: ' . $message, E_USER_DEPRECATED);
15908 }
15909 }
15910 }
15911 <?php
15912
15913 namespace Guzzle\Common;
15914
15915 use Symfony\Component\EventDispatcher\EventDispatcherInterface;
15916 use Symfony\Component\EventDispatcher\EventSubscriberInterface;
15917
15918 /**
15919 * Holds an event dispatcher
15920 */
15921 interface HasDispatcherInterface
15922 {
15923 /**
15924 * Get a list of all of the events emitted from the class
15925 *
15926 * @return array
15927 */
15928 public static function getAllEvents();
15929
15930 /**
15931 * Set the EventDispatcher of the request
15932 *
15933 * @param EventDispatcherInterface $eventDispatcher
15934 *
15935 * @return self
15936 */
15937 public function setEventDispatcher(EventDispatcherInterface $eventDispatcher);
15938
15939 /**
15940 * Get the EventDispatcher of the request
15941 *
15942 * @return EventDispatcherInterface
15943 */
15944 public function getEventDispatcher();
15945
15946 /**
15947 * Helper to dispatch Guzzle events and set the event name on the event
15948 *
15949 * @param string $eventName Name of the event to dispatch
15950 * @param array $context Context of the event
15951 *
15952 * @return Event Returns the created event object
15953 */
15954 public function dispatch($eventName, array $context = array());
15955
15956 /**
15957 * Add an event subscriber to the dispatcher
15958 *
15959 * @param EventSubscriberInterface $subscriber Event subscriber
15960 *
15961 * @return self
15962 */
15963 public function addSubscriber(EventSubscriberInterface $subscriber);
15964 }
15965 <?php
15966
15967 namespace Guzzle\Common;
15968
15969 use Symfony\Component\EventDispatcher\Event as SymfonyEvent;
15970
15971 /**
15972 * Default event for Guzzle notifications
15973 */
15974 class Event extends SymfonyEvent implements ToArrayInterface, \ArrayAccess, \IteratorAggregate
15975 {
15976 /** @var array */
15977 private $context;
15978
15979 /**
15980 * @param array $context Contextual information
15981 */
15982 public function __construct(array $context = array())
15983 {
15984 $this->context = $context;
15985 }
15986
15987 public function getIterator()
15988 {
15989 return new \ArrayIterator($this->context);
15990 }
15991
15992 public function offsetGet($offset)
15993 {
15994 return isset($this->context[$offset]) ? $this->context[$offset] : null;
15995 }
15996
15997 public function offsetSet($offset, $value)
15998 {
15999 $this->context[$offset] = $value;
16000 }
16001
16002 public function offsetExists($offset)
16003 {
16004 return isset($this->context[$offset]);
16005 }
16006
16007 public function offsetUnset($offset)
16008 {
16009 unset($this->context[$offset]);
16010 }
16011
16012 public function toArray()
16013 {
16014 return $this->context;
16015 }
16016 }
16017 <?php
16018
16019 namespace Guzzle\Common;
16020
16021 /**
16022 * Interfaces that adds a factory method which is used to instantiate a class from an array of configuration options.
16023 */
16024 interface FromConfigInterface
16025 {
16026 /**
16027 * Static factory method used to turn an array or collection of configuration data into an instantiated object.
16028 *
16029 * @param array|Collection $config Configuration data
16030 *
16031 * @return FromConfigInterface
16032 */
16033 public static function factory($config = array());
16034 }
16035 <?php
16036
16037 namespace Guzzle\Common\Exception;
16038
16039 class RuntimeException extends \RuntimeException implements GuzzleException {}
16040 <?php
16041
16042 namespace Guzzle\Common\Exception;
16043
16044 class InvalidArgumentException extends \InvalidArgumentException implements GuzzleException {}
16045 <?php
16046
16047 namespace Guzzle\Common\Exception;
16048
16049 /**
16050 * Collection of exceptions
16051 */
16052 class ExceptionCollection extends \Exception implements GuzzleException, \IteratorAggregate, \Countable
16053 {
16054 /** @var array Array of Exceptions */
16055 protected $exceptions = array();
16056
16057 /**
16058 * Set all of the exceptions
16059 *
16060 * @param array $exceptions Array of exceptions
16061 *
16062 * @return self
16063 */
16064 public function setExceptions(array $exceptions)
16065 {
16066 $this->exceptions = array();
16067 foreach ($exceptions as $exception) {
16068 $this->add($exception);
16069 }
16070
16071 return $this;
16072 }
16073
16074 /**
16075 * Add exceptions to the collection
16076 *
16077 * @param ExceptionCollection|\Exception $e Exception to add
16078 *
16079 * @return ExceptionCollection;
16080 */
16081 public function add($e)
16082 {
16083 if ($this->message) {
16084 $this->message .= "\n";
16085 }
16086
16087 if ($e instanceof self) {
16088 $this->message .= '(' . get_class($e) . ")";
16089 foreach (explode("\n", $e->getMessage()) as $message) {
16090 $this->message .= "\n {$message}";
16091 }
16092 } elseif ($e instanceof \Exception) {
16093 $this->exceptions[] = $e;
16094 $this->message .= '(' . get_class($e) . ') ' . $e->getMessage();
16095 }
16096
16097 return $this;
16098 }
16099
16100 /**
16101 * Get the total number of request exceptions
16102 *
16103 * @return int
16104 */
16105 public function count()
16106 {
16107 return count($this->exceptions);
16108 }
16109
16110 /**
16111 * Allows array-like iteration over the request exceptions
16112 *
16113 * @return \ArrayIterator
16114 */
16115 public function getIterator()
16116 {
16117 return new \ArrayIterator($this->exceptions);
16118 }
16119
16120 /**
16121 * Get the first exception in the collection
16122 *
16123 * @return \Exception
16124 */
16125 public function getFirst()
16126 {
16127 return $this->exceptions ? $this->exceptions[0] : null;
16128 }
16129 }
16130 <?php
16131
16132 namespace Guzzle\Common\Exception;
16133
16134 class BadMethodCallException extends \BadMethodCallException implements GuzzleException {}
16135 <?php
16136
16137 namespace Guzzle\Common\Exception;
16138
16139 class UnexpectedValueException extends \UnexpectedValueException implements GuzzleException {}
16140 <?php
16141
16142 namespace Guzzle\Common\Exception;
16143
16144 /**
16145 * Guzzle exception
16146 */
16147 interface GuzzleException {}
16148 <?php
16149
16150 namespace Guzzle\Common;
16151
16152 use Guzzle\Common\Exception\InvalidArgumentException;
16153 use Guzzle\Common\Exception\RuntimeException;
16154
16155 /**
16156 * Key value pair collection object
16157 */
16158 class Collection implements \ArrayAccess, \IteratorAggregate, \Countable, ToArrayInterface
16159 {
16160 /** @var array Data associated with the object. */
16161 protected $data;
16162
16163 /**
16164 * @param array $data Associative array of data to set
16165 */
16166 public function __construct(array $data = array())
16167 {
16168 $this->data = $data;
16169 }
16170
16171 /**
16172 * Create a new collection from an array, validate the keys, and add default values where missing
16173 *
16174 * @param array $config Configuration values to apply.
16175 * @param array $defaults Default parameters
16176 * @param array $required Required parameter names
16177 *
16178 * @return self
16179 * @throws InvalidArgumentException if a parameter is missing
16180 */
16181 public static function fromConfig(array $config = array(), array $defaults = array(), array $required = array())
16182 {
16183 $data = $config + $defaults;
16184
16185 if ($missing = array_diff($required, array_keys($data))) {
16186 throw new InvalidArgumentException('Config is missing the following keys: ' . implode(', ', $missing));
16187 }
16188
16189 return new self($data);
16190 }
16191
16192 public function count()
16193 {
16194 return count($this->data);
16195 }
16196
16197 public function getIterator()
16198 {
16199 return new \ArrayIterator($this->data);
16200 }
16201
16202 public function toArray()
16203 {
16204 return $this->data;
16205 }
16206
16207 /**
16208 * Removes all key value pairs
16209 *
16210 * @return Collection
16211 */
16212 public function clear()
16213 {
16214 $this->data = array();
16215
16216 return $this;
16217 }
16218
16219 /**
16220 * Get all or a subset of matching key value pairs
16221 *
16222 * @param array $keys Pass an array of keys to retrieve only a subset of key value pairs
16223 *
16224 * @return array Returns an array of all matching key value pairs
16225 */
16226 public function getAll(array $keys = null)
16227 {
16228 return $keys ? array_intersect_key($this->data, array_flip($keys)) : $this->data;
16229 }
16230
16231 /**
16232 * Get a specific key value.
16233 *
16234 * @param string $key Key to retrieve.
16235 *
16236 * @return mixed|null Value of the key or NULL
16237 */
16238 public function get($key)
16239 {
16240 return isset($this->data[$key]) ? $this->data[$key] : null;
16241 }
16242
16243 /**
16244 * Set a key value pair
16245 *
16246 * @param string $key Key to set
16247 * @param mixed $value Value to set
16248 *
16249 * @return Collection Returns a reference to the object
16250 */
16251 public function set($key, $value)
16252 {
16253 $this->data[$key] = $value;
16254
16255 return $this;
16256 }
16257
16258 /**
16259 * Add a value to a key. If a key of the same name has already been added, the key value will be converted into an
16260 * array and the new value will be pushed to the end of the array.
16261 *
16262 * @param string $key Key to add
16263 * @param mixed $value Value to add to the key
16264 *
16265 * @return Collection Returns a reference to the object.
16266 */
16267 public function add($key, $value)
16268 {
16269 if (!array_key_exists($key, $this->data)) {
16270 $this->data[$key] = $value;
16271 } elseif (is_array($this->data[$key])) {
16272 $this->data[$key][] = $value;
16273 } else {
16274 $this->data[$key] = array($this->data[$key], $value);
16275 }
16276
16277 return $this;
16278 }
16279
16280 /**
16281 * Remove a specific key value pair
16282 *
16283 * @param string $key A key to remove
16284 *
16285 * @return Collection
16286 */
16287 public function remove($key)
16288 {
16289 unset($this->data[$key]);
16290
16291 return $this;
16292 }
16293
16294 /**
16295 * Get all keys in the collection
16296 *
16297 * @return array
16298 */
16299 public function getKeys()
16300 {
16301 return array_keys($this->data);
16302 }
16303
16304 /**
16305 * Returns whether or not the specified key is present.
16306 *
16307 * @param string $key The key for which to check the existence.
16308 *
16309 * @return bool
16310 */
16311 public function hasKey($key)
16312 {
16313 return array_key_exists($key, $this->data);
16314 }
16315
16316 /**
16317 * Case insensitive search the keys in the collection
16318 *
16319 * @param string $key Key to search for
16320 *
16321 * @return bool|string Returns false if not found, otherwise returns the key
16322 */
16323 public function keySearch($key)
16324 {
16325 foreach (array_keys($this->data) as $k) {
16326 if (!strcasecmp($k, $key)) {
16327 return $k;
16328 }
16329 }
16330
16331 return false;
16332 }
16333
16334 /**
16335 * Checks if any keys contains a certain value
16336 *
16337 * @param string $value Value to search for
16338 *
16339 * @return mixed Returns the key if the value was found FALSE if the value was not found.
16340 */
16341 public function hasValue($value)
16342 {
16343 return array_search($value, $this->data);
16344 }
16345
16346 /**
16347 * Replace the data of the object with the value of an array
16348 *
16349 * @param array $data Associative array of data
16350 *
16351 * @return Collection Returns a reference to the object
16352 */
16353 public function replace(array $data)
16354 {
16355 $this->data = $data;
16356
16357 return $this;
16358 }
16359
16360 /**
16361 * Add and merge in a Collection or array of key value pair data.
16362 *
16363 * @param Collection|array $data Associative array of key value pair data
16364 *
16365 * @return Collection Returns a reference to the object.
16366 */
16367 public function merge($data)
16368 {
16369 foreach ($data as $key => $value) {
16370 $this->add($key, $value);
16371 }
16372
16373 return $this;
16374 }
16375
16376 /**
16377 * Over write key value pairs in this collection with all of the data from an array or collection.
16378 *
16379 * @param array|\Traversable $data Values to override over this config
16380 *
16381 * @return self
16382 */
16383 public function overwriteWith($data)
16384 {
16385 if (is_array($data)) {
16386 $this->data = $data + $this->data;
16387 } elseif ($data instanceof Collection) {
16388 $this->data = $data->toArray() + $this->data;
16389 } else {
16390 foreach ($data as $key => $value) {
16391 $this->data[$key] = $value;
16392 }
16393 }
16394
16395 return $this;
16396 }
16397
16398 /**
16399 * Returns a Collection containing all the elements of the collection after applying the callback function to each
16400 * one. The Closure should accept three parameters: (string) $key, (string) $value, (array) $context and return a
16401 * modified value
16402 *
16403 * @param \Closure $closure Closure to apply
16404 * @param array $context Context to pass to the closure
16405 * @param bool $static Set to TRUE to use the same class as the return rather than returning a Collection
16406 *
16407 * @return Collection
16408 */
16409 public function map(\Closure $closure, array $context = array(), $static = true)
16410 {
16411 $collection = $static ? new static() : new self();
16412 foreach ($this as $key => $value) {
16413 $collection->add($key, $closure($key, $value, $context));
16414 }
16415
16416 return $collection;
16417 }
16418
16419 /**
16420 * Iterates over each key value pair in the collection passing them to the Closure. If the Closure function returns
16421 * true, the current value from input is returned into the result Collection. The Closure must accept three
16422 * parameters: (string) $key, (string) $value and return Boolean TRUE or FALSE for each value.
16423 *
16424 * @param \Closure $closure Closure evaluation function
16425 * @param bool $static Set to TRUE to use the same class as the return rather than returning a Collection
16426 *
16427 * @return Collection
16428 */
16429 public function filter(\Closure $closure, $static = true)
16430 {
16431 $collection = ($static) ? new static() : new self();
16432 foreach ($this->data as $key => $value) {
16433 if ($closure($key, $value)) {
16434 $collection->add($key, $value);
16435 }
16436 }
16437
16438 return $collection;
16439 }
16440
16441 public function offsetExists($offset)
16442 {
16443 return isset($this->data[$offset]);
16444 }
16445
16446 public function offsetGet($offset)
16447 {
16448 return isset($this->data[$offset]) ? $this->data[$offset] : null;
16449 }
16450
16451 public function offsetSet($offset, $value)
16452 {
16453 $this->data[$offset] = $value;
16454 }
16455
16456 public function offsetUnset($offset)
16457 {
16458 unset($this->data[$offset]);
16459 }
16460
16461 /**
16462 * Set a value into a nested array key. Keys will be created as needed to set the value.
16463 *
16464 * @param string $path Path to set
16465 * @param mixed $value Value to set at the key
16466 *
16467 * @return self
16468 * @throws RuntimeException when trying to setPath using a nested path that travels through a scalar value
16469 */
16470 public function setPath($path, $value)
16471 {
16472 $current =& $this->data;
16473 $queue = explode('/', $path);
16474 while (null !== ($key = array_shift($queue))) {
16475 if (!is_array($current)) {
16476 throw new RuntimeException("Trying to setPath {$path}, but {$key} is set and is not an array");
16477 } elseif (!$queue) {
16478 $current[$key] = $value;
16479 } elseif (isset($current[$key])) {
16480 $current =& $current[$key];
16481 } else {
16482 $current[$key] = array();
16483 $current =& $current[$key];
16484 }
16485 }
16486
16487 return $this;
16488 }
16489
16490 /**
16491 * Gets a value from the collection using an array path (e.g. foo/baz/bar would retrieve bar from two nested arrays)
16492 * Allows for wildcard searches which recursively combine matches up to the level at which the wildcard occurs. This
16493 * can be useful for accepting any key of a sub-array and combining matching keys from each diverging path.
16494 *
16495 * @param string $path Path to traverse and retrieve a value from
16496 * @param string $separator Character used to add depth to the search
16497 * @param mixed $data Optional data to descend into (used when wildcards are encountered)
16498 *
16499 * @return mixed|null
16500 */
16501 public function getPath($path, $separator = '/', $data = null)
16502 {
16503 if ($data === null) {
16504 $data =& $this->data;
16505 }
16506
16507 $path = is_array($path) ? $path : explode($separator, $path);
16508 while (null !== ($part = array_shift($path))) {
16509 if (!is_array($data)) {
16510 return null;
16511 } elseif (isset($data[$part])) {
16512 $data =& $data[$part];
16513 } elseif ($part != '*') {
16514 return null;
16515 } else {
16516 // Perform a wildcard search by diverging and merging paths
16517 $result = array();
16518 foreach ($data as $value) {
16519 if (!$path) {
16520 $result = array_merge_recursive($result, (array) $value);
16521 } elseif (null !== ($test = $this->getPath($path, $separator, $value))) {
16522 $result = array_merge_recursive($result, (array) $test);
16523 }
16524 }
16525 return $result;
16526 }
16527 }
16528
16529 return $data;
16530 }
16531
16532 /**
16533 * Inject configuration settings into an input string
16534 *
16535 * @param string $input Input to inject
16536 *
16537 * @return string
16538 * @deprecated
16539 */
16540 public function inject($input)
16541 {
16542 Version::warn(__METHOD__ . ' is deprecated');
16543 $replace = array();
16544 foreach ($this->data as $key => $val) {
16545 $replace['{' . $key . '}'] = $val;
16546 }
16547
16548 return strtr($input, $replace);
16549 }
16550 }
16551 <?php
16552
16553 namespace Guzzle\Common;
16554
16555 /**
16556 * An object that can be represented as an array
16557 */
16558 interface ToArrayInterface
16559 {
16560 /**
16561 * Get the array representation of an object
16562 *
16563 * @return array
16564 */
16565 public function toArray();
16566 }
16567 <?php
16568
16569 namespace Guzzle\Common;
16570
16571 use Symfony\Component\EventDispatcher\EventDispatcher;
16572 use Symfony\Component\EventDispatcher\EventDispatcherInterface;
16573 use Symfony\Component\EventDispatcher\EventSubscriberInterface;
16574
16575 /**
16576 * Class that holds an event dispatcher
16577 */
16578 class AbstractHasDispatcher implements HasDispatcherInterface
16579 {
16580 /** @var EventDispatcherInterface */
16581 protected $eventDispatcher;
16582
16583 public static function getAllEvents()
16584 {
16585 return array();
16586 }
16587
16588 public function setEventDispatcher(EventDispatcherInterface $eventDispatcher)
16589 {
16590 $this->eventDispatcher = $eventDispatcher;
16591
16592 return $this;
16593 }
16594
16595 public function getEventDispatcher()
16596 {
16597 if (!$this->eventDispatcher) {
16598 $this->eventDispatcher = new EventDispatcher();
16599 }
16600
16601 return $this->eventDispatcher;
16602 }
16603
16604 public function dispatch($eventName, array $context = array())
16605 {
16606 return $this->getEventDispatcher()->dispatch($eventName, new Event($context));
16607 }
16608
16609 public function addSubscriber(EventSubscriberInterface $subscriber)
16610 {
16611 $this->getEventDispatcher()->addSubscriber($subscriber);
16612
16613 return $this;
16614 }
16615 }
16616 {
16617 "name": "guzzle/common",
16618 "homepage": "http://guzzlephp.org/",
16619 "description": "Common libraries used by Guzzle",
16620 "keywords": ["common", "event", "exception", "collection"],
16621 "license": "MIT",
16622 "require": {
16623 "php": ">=5.3.2",
16624 "symfony/event-dispatcher": ">=2.1"
16625 },
16626 "autoload": {
16627 "psr-0": { "Guzzle\\Common": "" }
16628 },
16629 "target-dir": "Guzzle/Common",
16630 "extra": {
16631 "branch-alias": {
16632 "dev-master": "3.7-dev"
16633 }
16634 }
16635 }
16636 <?php
16637
16638 namespace Guzzle\Service;
16639
16640 /**
16641 * Interface used for loading configuration data (service descriptions, service builder configs, etc)
16642 *
16643 * If a loaded configuration data sets includes a top level key containing an 'includes' section, then the data in the
16644 * file will extend the merged result of all of the included config files.
16645 */
16646 interface ConfigLoaderInterface
16647 {
16648 /**
16649 * Loads configuration data and returns an array of the loaded result
16650 *
16651 * @param mixed $config Data to load (filename or array of data)
16652 * @param array $options Array of options to use when loading
16653 *
16654 * @return mixed
16655 */
16656 public function load($config, array $options = array());
16657 }
16658 <?php
16659
16660 namespace Guzzle\Service\Description;
16661
16662 /**
16663 * Validator responsible for preparing and validating parameters against the parameter's schema
16664 */
16665 interface ValidatorInterface
16666 {
16667 /**
16668 * Validate a value against the acceptable types, regular expressions, minimum, maximums, instanceOf, enums, etc
16669 * Add default and static values to the passed in variable. If the validation completes successfully, the input
16670 * must be run correctly through the matching schema's filters attribute.
16671 *
16672 * @param Parameter $param Schema that is being validated against the value
16673 * @param mixed $value Value to validate and process. The value may change during this process.
16674 *
16675 * @return bool Returns true if the input data is valid for the schema
16676 */
16677 public function validate(Parameter $param, &$value);
16678
16679 /**
16680 * Get validation errors encountered while validating
16681 *
16682 * @return array
16683 */
16684 public function getErrors();
16685 }
16686 <?php
16687
16688 namespace Guzzle\Service\Description;
16689
16690 use Guzzle\Service\AbstractConfigLoader;
16691 use Guzzle\Service\Exception\DescriptionBuilderException;
16692
16693 /**
16694 * Loader for service descriptions
16695 */
16696 class ServiceDescriptionLoader extends AbstractConfigLoader
16697 {
16698 protected function build($config, array $options)
16699 {
16700 $operations = array();
16701 if (!empty($config['operations'])) {
16702 foreach ($config['operations'] as $name => $op) {
16703 $name = $op['name'] = isset($op['name']) ? $op['name'] : $name;
16704 // Extend other operations
16705 if (!empty($op['extends'])) {
16706 $this->resolveExtension($name, $op, $operations);
16707 }
16708 $op['parameters'] = isset($op['parameters']) ? $op['parameters'] : array();
16709 $operations[$name] = $op;
16710 }
16711 }
16712
16713 return new ServiceDescription(array(
16714 'apiVersion' => isset($config['apiVersion']) ? $config['apiVersion'] : null,
16715 'baseUrl' => isset($config['baseUrl']) ? $config['baseUrl'] : null,
16716 'description' => isset($config['description']) ? $config['description'] : null,
16717 'operations' => $operations,
16718 'models' => isset($config['models']) ? $config['models'] : null
16719 ) + $config);
16720 }
16721
16722 /**
16723 * @param string $name Name of the operation
16724 * @param array $op Operation value array
16725 * @param array $operations Currently loaded operations
16726 * @throws DescriptionBuilderException when extending a non-existent operation
16727 */
16728 protected function resolveExtension($name, array &$op, array &$operations)
16729 {
16730 $resolved = array();
16731 $original = empty($op['parameters']) ? false: $op['parameters'];
16732 $hasClass = !empty($op['class']);
16733 foreach ((array) $op['extends'] as $extendedCommand) {
16734 if (empty($operations[$extendedCommand])) {
16735 throw new DescriptionBuilderException("{$name} extends missing operation {$extendedCommand}");
16736 }
16737 $toArray = $operations[$extendedCommand];
16738 $resolved = empty($resolved)
16739 ? $toArray['parameters']
16740 : array_merge($resolved, $toArray['parameters']);
16741
16742 $op = $op + $toArray;
16743 if (!$hasClass && isset($toArray['class'])) {
16744 $op['class'] = $toArray['class'];
16745 }
16746 }
16747 $op['parameters'] = $original ? array_merge($resolved, $original) : $resolved;
16748 }
16749 }
16750 <?php
16751
16752 namespace Guzzle\Service\Description;
16753
16754 use Guzzle\Common\Exception\InvalidArgumentException;
16755
16756 /**
16757 * API parameter object used with service descriptions
16758 */
16759 class Parameter
16760 {
16761 protected $name;
16762 protected $description;
16763 protected $serviceDescription;
16764 protected $type;
16765 protected $required;
16766 protected $enum;
16767 protected $pattern;
16768 protected $minimum;
16769 protected $maximum;
16770 protected $minLength;
16771 protected $maxLength;
16772 protected $minItems;
16773 protected $maxItems;
16774 protected $default;
16775 protected $static;
16776 protected $instanceOf;
16777 protected $filters;
16778 protected $location;
16779 protected $sentAs;
16780 protected $data;
16781 protected $properties = array();
16782 protected $additionalProperties;
16783 protected $items;
16784 protected $parent;
16785 protected $ref;
16786 protected $format;
16787 protected $propertiesCache = null;
16788
16789 /**
16790 * Create a new Parameter using an associative array of data. The array can contain the following information:
16791 * - name: (string) Unique name of the parameter
16792 * - type: (string|array) Type of variable (string, number, integer, boolean, object, array, numeric,
16793 * null, any). Types are using for validation and determining the structure of a parameter. You
16794 * can use a union type by providing an array of simple types. If one of the union types matches
16795 * the provided value, then the value is valid.
16796 * - instanceOf: (string) When the type is an object, you can specify the class that the object must implement
16797 * - required: (bool) Whether or not the parameter is required
16798 * - default: (mixed) Default value to use if no value is supplied
16799 * - static: (bool) Set to true to specify that the parameter value cannot be changed from the default
16800 * - description: (string) Documentation of the parameter
16801 * - location: (string) The location of a request used to apply a parameter. Custom locations can be registered
16802 * with a command, but the defaults are uri, query, header, body, json, xml, postField, postFile.
16803 * - sentAs: (string) Specifies how the data being modeled is sent over the wire. For example, you may wish
16804 * to include certain headers in a response model that have a normalized casing of FooBar, but the
16805 * actual header is x-foo-bar. In this case, sentAs would be set to x-foo-bar.
16806 * - filters: (array) Array of static method names to to run a parameter value through. Each value in the
16807 * array must be a string containing the full class path to a static method or an array of complex
16808 * filter information. You can specify static methods of classes using the full namespace class
16809 * name followed by '::' (e.g. Foo\Bar::baz()). Some filters require arguments in order to properly
16810 * filter a value. For complex filters, use a hash containing a 'method' key pointing to a static
16811 * method, and an 'args' key containing an array of positional arguments to pass to the method.
16812 * Arguments can contain keywords that are replaced when filtering a value: '@value' is replaced
16813 * with the value being validated, '@api' is replaced with the Parameter object.
16814 * - properties: When the type is an object, you can specify nested parameters
16815 * - additionalProperties: (array) This attribute defines a schema for all properties that are not explicitly
16816 * defined in an object type definition. If specified, the value MUST be a schema or a boolean. If
16817 * false is provided, no additional properties are allowed beyond the properties defined in the
16818 * schema. The default value is an empty schema which allows any value for additional properties.
16819 * - items: This attribute defines the allowed items in an instance array, and MUST be a schema or an array
16820 * of schemas. The default value is an empty schema which allows any value for items in the
16821 * instance array.
16822 * When this attribute value is a schema and the instance value is an array, then all the items
16823 * in the array MUST be valid according to the schema.
16824 * - pattern: When the type is a string, you can specify the regex pattern that a value must match
16825 * - enum: When the type is a string, you can specify a list of acceptable values
16826 * - minItems: (int) Minimum number of items allowed in an array
16827 * - maxItems: (int) Maximum number of items allowed in an array
16828 * - minLength: (int) Minimum length of a string
16829 * - maxLength: (int) Maximum length of a string
16830 * - minimum: (int) Minimum value of an integer
16831 * - maximum: (int) Maximum value of an integer
16832 * - data: (array) Any additional custom data to use when serializing, validating, etc
16833 * - format: (string) Format used to coax a value into the correct format when serializing or unserializing.
16834 * You may specify either an array of filters OR a format, but not both.
16835 * Supported values: date-time, date, time, timestamp, date-time-http
16836 * - $ref: (string) String referencing a service description model. The parameter is replaced by the
16837 * schema contained in the model.
16838 *
16839 * @param array $data Array of data as seen in service descriptions
16840 * @param ServiceDescriptionInterface $description Service description used to resolve models if $ref tags are found
16841 *
16842 * @throws InvalidArgumentException
16843 */
16844 public function __construct(array $data = array(), ServiceDescriptionInterface $description = null)
16845 {
16846 if ($description) {
16847 if (isset($data['$ref'])) {
16848 if ($model = $description->getModel($data['$ref'])) {
16849 // The name of the original parameter should override the ref name if one is available
16850 $name = empty($data['name']) ? null : $data['name'];
16851 $data = $model->toArray();
16852 if ($name) {
16853 $data['name'] = $name;
16854 }
16855 }
16856 } elseif (isset($data['extends'])) {
16857 // If this parameter extends from another parameter then start with the actual data
16858 // union in the parent's data (e.g. actual supersedes parent)
16859 if ($extends = $description->getModel($data['extends'])) {
16860 $data += $extends->toArray();
16861 }
16862 }
16863 }
16864
16865 // Pull configuration data into the parameter
16866 foreach ($data as $key => $value) {
16867 $this->{$key} = $value;
16868 }
16869
16870 $this->serviceDescription = $description;
16871 $this->required = (bool) $this->required;
16872 $this->data = (array) $this->data;
16873
16874 if ($this->filters) {
16875 $this->setFilters((array) $this->filters);
16876 }
16877
16878 if ($this->type == 'object' && $this->additionalProperties === null) {
16879 $this->additionalProperties = true;
16880 }
16881 }
16882
16883 /**
16884 * Convert the object to an array
16885 *
16886 * @return array
16887 */
16888 public function toArray()
16889 {
16890 static $checks = array('required', 'description', 'static', 'type', 'format', 'instanceOf', 'location', 'sentAs',
16891 'pattern', 'minimum', 'maximum', 'minItems', 'maxItems', 'minLength', 'maxLength', 'data', 'enum',
16892 'filters');
16893
16894 $result = array();
16895
16896 // Anything that is in the `Items` attribute of an array *must* include it's name if available
16897 if ($this->parent instanceof self && $this->parent->getType() == 'array' && isset($this->name)) {
16898 $result['name'] = $this->name;
16899 }
16900
16901 foreach ($checks as $c) {
16902 if ($value = $this->{$c}) {
16903 $result[$c] = $value;
16904 }
16905 }
16906
16907 if ($this->default !== null) {
16908 $result['default'] = $this->default;
16909 }
16910
16911 if ($this->items !== null) {
16912 $result['items'] = $this->getItems()->toArray();
16913 }
16914
16915 if ($this->additionalProperties !== null) {
16916 $result['additionalProperties'] = $this->getAdditionalProperties();
16917 if ($result['additionalProperties'] instanceof self) {
16918 $result['additionalProperties'] = $result['additionalProperties']->toArray();
16919 }
16920 }
16921
16922 if ($this->type == 'object' && $this->properties) {
16923 $result['properties'] = array();
16924 foreach ($this->getProperties() as $name => $property) {
16925 $result['properties'][$name] = $property->toArray();
16926 }
16927 }
16928
16929 return $result;
16930 }
16931
16932 /**
16933 * Get the default or static value of the command based on a value
16934 *
16935 * @param string $value Value that is currently set
16936 *
16937 * @return mixed Returns the value, a static value if one is present, or a default value
16938 */
16939 public function getValue($value)
16940 {
16941 return $this->static || ($this->default !== null && !$value && ($this->type != 'boolean' || $value !== false))
16942 ? $this->default
16943 : $value;
16944 }
16945
16946 /**
16947 * Run a value through the filters OR format attribute associated with the parameter
16948 *
16949 * @param mixed $value Value to filter
16950 *
16951 * @return mixed Returns the filtered value
16952 */
16953 public function filter($value)
16954 {
16955 // Formats are applied exclusively and supersed filters
16956 if ($this->format) {
16957 return SchemaFormatter::format($this->format, $value);
16958 }
16959
16960 // Convert Boolean values
16961 if ($this->type == 'boolean' && !is_bool($value)) {
16962 $value = filter_var($value, FILTER_VALIDATE_BOOLEAN);
16963 }
16964
16965 // Apply filters to the value
16966 if ($this->filters) {
16967 foreach ($this->filters as $filter) {
16968 if (is_array($filter)) {
16969 // Convert complex filters that hold value place holders
16970 foreach ($filter['args'] as &$data) {
16971 if ($data == '@value') {
16972 $data = $value;
16973 } elseif ($data == '@api') {
16974 $data = $this;
16975 }
16976 }
16977 $value = call_user_func_array($filter['method'], $filter['args']);
16978 } else {
16979 $value = call_user_func($filter, $value);
16980 }
16981 }
16982 }
16983
16984 return $value;
16985 }
16986
16987 /**
16988 * Get the name of the parameter
16989 *
16990 * @return string
16991 */
16992 public function getName()
16993 {
16994 return $this->name;
16995 }
16996
16997 /**
16998 * Get the key of the parameter, where sentAs will supersede name if it is set
16999 *
17000 * @return string
17001 */
17002 public function getWireName()
17003 {
17004 return $this->sentAs ?: $this->name;
17005 }
17006
17007 /**
17008 * Set the name of the parameter
17009 *
17010 * @param string $name Name to set
17011 *
17012 * @return self
17013 */
17014 public function setName($name)
17015 {
17016 $this->name = $name;
17017
17018 return $this;
17019 }
17020
17021 /**
17022 * Get the type(s) of the parameter
17023 *
17024 * @return string|array
17025 */
17026 public function getType()
17027 {
17028 return $this->type;
17029 }
17030
17031 /**
17032 * Set the type(s) of the parameter
17033 *
17034 * @param string|array $type Type of parameter or array of simple types used in a union
17035 *
17036 * @return self
17037 */
17038 public function setType($type)
17039 {
17040 $this->type = $type;
17041
17042 return $this;
17043 }
17044
17045 /**
17046 * Get if the parameter is required
17047 *
17048 * @return bool
17049 */
17050 public function getRequired()
17051 {
17052 return $this->required;
17053 }
17054
17055 /**
17056 * Set if the parameter is required
17057 *
17058 * @param bool $isRequired Whether or not the parameter is required
17059 *
17060 * @return self
17061 */
17062 public function setRequired($isRequired)
17063 {
17064 $this->required = (bool) $isRequired;
17065
17066 return $this;
17067 }
17068
17069 /**
17070 * Get the default value of the parameter
17071 *
17072 * @return string|null
17073 */
17074 public function getDefault()
17075 {
17076 return $this->default;
17077 }
17078
17079 /**
17080 * Set the default value of the parameter
17081 *
17082 * @param string|null $default Default value to set
17083 *
17084 * @return self
17085 */
17086 public function setDefault($default)
17087 {
17088 $this->default = $default;
17089
17090 return $this;
17091 }
17092
17093 /**
17094 * Get the description of the parameter
17095 *
17096 * @return string|null
17097 */
17098 public function getDescription()
17099 {
17100 return $this->description;
17101 }
17102
17103 /**
17104 * Set the description of the parameter
17105 *
17106 * @param string $description Description
17107 *
17108 * @return self
17109 */
17110 public function setDescription($description)
17111 {
17112 $this->description = $description;
17113
17114 return $this;
17115 }
17116
17117 /**
17118 * Get the minimum acceptable value for an integer
17119 *
17120 * @return int|null
17121 */
17122 public function getMinimum()
17123 {
17124 return $this->minimum;
17125 }
17126
17127 /**
17128 * Set the minimum acceptable value for an integer
17129 *
17130 * @param int|null $min Minimum
17131 *
17132 * @return self
17133 */
17134 public function setMinimum($min)
17135 {
17136 $this->minimum = $min;
17137
17138 return $this;
17139 }
17140
17141 /**
17142 * Get the maximum acceptable value for an integer
17143 *
17144 * @return int|null
17145 */
17146 public function getMaximum()
17147 {
17148 return $this->maximum;
17149 }
17150
17151 /**
17152 * Set the maximum acceptable value for an integer
17153 *
17154 * @param int $max Maximum
17155 *
17156 * @return self
17157 */
17158 public function setMaximum($max)
17159 {
17160 $this->maximum = $max;
17161
17162 return $this;
17163 }
17164
17165 /**
17166 * Get the minimum allowed length of a string value
17167 *
17168 * @return int
17169 */
17170 public function getMinLength()
17171 {
17172 return $this->minLength;
17173 }
17174
17175 /**
17176 * Set the minimum allowed length of a string value
17177 *
17178 * @param int|null $min Minimum
17179 *
17180 * @return self
17181 */
17182 public function setMinLength($min)
17183 {
17184 $this->minLength = $min;
17185
17186 return $this;
17187 }
17188
17189 /**
17190 * Get the maximum allowed length of a string value
17191 *
17192 * @return int|null
17193 */
17194 public function getMaxLength()
17195 {
17196 return $this->maxLength;
17197 }
17198
17199 /**
17200 * Set the maximum allowed length of a string value
17201 *
17202 * @param int $max Maximum length
17203 *
17204 * @return self
17205 */
17206 public function setMaxLength($max)
17207 {
17208 $this->maxLength = $max;
17209
17210 return $this;
17211 }
17212
17213 /**
17214 * Get the maximum allowed number of items in an array value
17215 *
17216 * @return int|null
17217 */
17218 public function getMaxItems()
17219 {
17220 return $this->maxItems;
17221 }
17222
17223 /**
17224 * Set the maximum allowed number of items in an array value
17225 *
17226 * @param int $max Maximum
17227 *
17228 * @return self
17229 */
17230 public function setMaxItems($max)
17231 {
17232 $this->maxItems = $max;
17233
17234 return $this;
17235 }
17236
17237 /**
17238 * Get the minimum allowed number of items in an array value
17239 *
17240 * @return int
17241 */
17242 public function getMinItems()
17243 {
17244 return $this->minItems;
17245 }
17246
17247 /**
17248 * Set the minimum allowed number of items in an array value
17249 *
17250 * @param int|null $min Minimum
17251 *
17252 * @return self
17253 */
17254 public function setMinItems($min)
17255 {
17256 $this->minItems = $min;
17257
17258 return $this;
17259 }
17260
17261 /**
17262 * Get the location of the parameter
17263 *
17264 * @return string|null
17265 */
17266 public function getLocation()
17267 {
17268 return $this->location;
17269 }
17270
17271 /**
17272 * Set the location of the parameter
17273 *
17274 * @param string|null $location Location of the parameter
17275 *
17276 * @return self
17277 */
17278 public function setLocation($location)
17279 {
17280 $this->location = $location;
17281
17282 return $this;
17283 }
17284
17285 /**
17286 * Get the sentAs attribute of the parameter that used with locations to sentAs an attribute when it is being
17287 * applied to a location.
17288 *
17289 * @return string|null
17290 */
17291 public function getSentAs()
17292 {
17293 return $this->sentAs;
17294 }
17295
17296 /**
17297 * Set the sentAs attribute
17298 *
17299 * @param string|null $name Name of the value as it is sent over the wire
17300 *
17301 * @return self
17302 */
17303 public function setSentAs($name)
17304 {
17305 $this->sentAs = $name;
17306
17307 return $this;
17308 }
17309
17310 /**
17311 * Retrieve a known property from the parameter by name or a data property by name. When not specific name value
17312 * is specified, all data properties will be returned.
17313 *
17314 * @param string|null $name Specify a particular property name to retrieve
17315 *
17316 * @return array|mixed|null
17317 */
17318 public function getData($name = null)
17319 {
17320 if (!$name) {
17321 return $this->data;
17322 }
17323
17324 if (isset($this->data[$name])) {
17325 return $this->data[$name];
17326 } elseif (isset($this->{$name})) {
17327 return $this->{$name};
17328 }
17329
17330 return null;
17331 }
17332
17333 /**
17334 * Set the extra data properties of the parameter or set a specific extra property
17335 *
17336 * @param string|array|null $nameOrData The name of a specific extra to set or an array of extras to set
17337 * @param mixed|null $data When setting a specific extra property, specify the data to set for it
17338 *
17339 * @return self
17340 */
17341 public function setData($nameOrData, $data = null)
17342 {
17343 if (is_array($nameOrData)) {
17344 $this->data = $nameOrData;
17345 } else {
17346 $this->data[$nameOrData] = $data;
17347 }
17348
17349 return $this;
17350 }
17351
17352 /**
17353 * Get whether or not the default value can be changed
17354 *
17355 * @return mixed|null
17356 */
17357 public function getStatic()
17358 {
17359 return $this->static;
17360 }
17361
17362 /**
17363 * Set to true if the default value cannot be changed
17364 *
17365 * @param bool $static True or false
17366 *
17367 * @return self
17368 */
17369 public function setStatic($static)
17370 {
17371 $this->static = (bool) $static;
17372
17373 return $this;
17374 }
17375
17376 /**
17377 * Get an array of filters used by the parameter
17378 *
17379 * @return array
17380 */
17381 public function getFilters()
17382 {
17383 return $this->filters ?: array();
17384 }
17385
17386 /**
17387 * Set the array of filters used by the parameter
17388 *
17389 * @param array $filters Array of functions to use as filters
17390 *
17391 * @return self
17392 */
17393 public function setFilters(array $filters)
17394 {
17395 $this->filters = array();
17396 foreach ($filters as $filter) {
17397 $this->addFilter($filter);
17398 }
17399
17400 return $this;
17401 }
17402
17403 /**
17404 * Add a filter to the parameter
17405 *
17406 * @param string|array $filter Method to filter the value through
17407 *
17408 * @return self
17409 * @throws InvalidArgumentException
17410 */
17411 public function addFilter($filter)
17412 {
17413 if (is_array($filter)) {
17414 if (!isset($filter['method'])) {
17415 throw new InvalidArgumentException('A [method] value must be specified for each complex filter');
17416 }
17417 }
17418
17419 if (!$this->filters) {
17420 $this->filters = array($filter);
17421 } else {
17422 $this->filters[] = $filter;
17423 }
17424
17425 return $this;
17426 }
17427
17428 /**
17429 * Get the parent object (an {@see OperationInterface} or {@see Parameter}
17430 *
17431 * @return OperationInterface|Parameter|null
17432 */
17433 public function getParent()
17434 {
17435 return $this->parent;
17436 }
17437
17438 /**
17439 * Set the parent object of the parameter
17440 *
17441 * @param OperationInterface|Parameter|null $parent Parent container of the parameter
17442 *
17443 * @return self
17444 */
17445 public function setParent($parent)
17446 {
17447 $this->parent = $parent;
17448
17449 return $this;
17450 }
17451
17452 /**
17453 * Get the properties of the parameter
17454 *
17455 * @return array
17456 */
17457 public function getProperties()
17458 {
17459 if (!$this->propertiesCache) {
17460 $this->propertiesCache = array();
17461 foreach (array_keys($this->properties) as $name) {
17462 $this->propertiesCache[$name] = $this->getProperty($name);
17463 }
17464 }
17465
17466 return $this->propertiesCache;
17467 }
17468
17469 /**
17470 * Get a specific property from the parameter
17471 *
17472 * @param string $name Name of the property to retrieve
17473 *
17474 * @return null|Parameter
17475 */
17476 public function getProperty($name)
17477 {
17478 if (!isset($this->properties[$name])) {
17479 return null;
17480 }
17481
17482 if (!($this->properties[$name] instanceof self)) {
17483 $this->properties[$name]['name'] = $name;
17484 $this->properties[$name] = new static($this->properties[$name], $this->serviceDescription);
17485 $this->properties[$name]->setParent($this);
17486 }
17487
17488 return $this->properties[$name];
17489 }
17490
17491 /**
17492 * Remove a property from the parameter
17493 *
17494 * @param string $name Name of the property to remove
17495 *
17496 * @return self
17497 */
17498 public function removeProperty($name)
17499 {
17500 unset($this->properties[$name]);
17501 $this->propertiesCache = null;
17502
17503 return $this;
17504 }
17505
17506 /**
17507 * Add a property to the parameter
17508 *
17509 * @param Parameter $property Properties to set
17510 *
17511 * @return self
17512 */
17513 public function addProperty(Parameter $property)
17514 {
17515 $this->properties[$property->getName()] = $property;
17516 $property->setParent($this);
17517 $this->propertiesCache = null;
17518
17519 return $this;
17520 }
17521
17522 /**
17523 * Get the additionalProperties value of the parameter
17524 *
17525 * @return bool|Parameter|null
17526 */
17527 public function getAdditionalProperties()
17528 {
17529 if (is_array($this->additionalProperties)) {
17530 $this->additionalProperties = new static($this->additionalProperties, $this->serviceDescription);
17531 $this->additionalProperties->setParent($this);
17532 }
17533
17534 return $this->additionalProperties;
17535 }
17536
17537 /**
17538 * Set the additionalProperties value of the parameter
17539 *
17540 * @param bool|Parameter|null $additional Boolean to allow any, an Parameter to specify a schema, or false to disallow
17541 *
17542 * @return self
17543 */
17544 public function setAdditionalProperties($additional)
17545 {
17546 $this->additionalProperties = $additional;
17547
17548 return $this;
17549 }
17550
17551 /**
17552 * Set the items data of the parameter
17553 *
17554 * @param Parameter|null $items Items to set
17555 *
17556 * @return self
17557 */
17558 public function setItems(Parameter $items = null)
17559 {
17560 if ($this->items = $items) {
17561 $this->items->setParent($this);
17562 }
17563
17564 return $this;
17565 }
17566
17567 /**
17568 * Get the item data of the parameter
17569 *
17570 * @return Parameter|null
17571 */
17572 public function getItems()
17573 {
17574 if (is_array($this->items)) {
17575 $this->items = new static($this->items, $this->serviceDescription);
17576 $this->items->setParent($this);
17577 }
17578
17579 return $this->items;
17580 }
17581
17582 /**
17583 * Get the class that the parameter must implement
17584 *
17585 * @return null|string
17586 */
17587 public function getInstanceOf()
17588 {
17589 return $this->instanceOf;
17590 }
17591
17592 /**
17593 * Set the class that the parameter must be an instance of
17594 *
17595 * @param string|null $instanceOf Class or interface name
17596 *
17597 * @return self
17598 */
17599 public function setInstanceOf($instanceOf)
17600 {
17601 $this->instanceOf = $instanceOf;
17602
17603 return $this;
17604 }
17605
17606 /**
17607 * Get the enum of strings that are valid for the parameter
17608 *
17609 * @return array|null
17610 */
17611 public function getEnum()
17612 {
17613 return $this->enum;
17614 }
17615
17616 /**
17617 * Set the enum of strings that are valid for the parameter
17618 *
17619 * @param array|null $enum Array of strings or null
17620 *
17621 * @return self
17622 */
17623 public function setEnum(array $enum = null)
17624 {
17625 $this->enum = $enum;
17626
17627 return $this;
17628 }
17629
17630 /**
17631 * Get the regex pattern that must match a value when the value is a string
17632 *
17633 * @return string
17634 */
17635 public function getPattern()
17636 {
17637 return $this->pattern;
17638 }
17639
17640 /**
17641 * Set the regex pattern that must match a value when the value is a string
17642 *
17643 * @param string $pattern Regex pattern
17644 *
17645 * @return self
17646 */
17647 public function setPattern($pattern)
17648 {
17649 $this->pattern = $pattern;
17650
17651 return $this;
17652 }
17653
17654 /**
17655 * Get the format attribute of the schema
17656 *
17657 * @return string
17658 */
17659 public function getFormat()
17660 {
17661 return $this->format;
17662 }
17663
17664 /**
17665 * Set the format attribute of the schema
17666 *
17667 * @param string $format Format to set (e.g. date, date-time, timestamp, time, date-time-http)
17668 *
17669 * @return self
17670 */
17671 public function setFormat($format)
17672 {
17673 $this->format = $format;
17674
17675 return $this;
17676 }
17677 }
17678 <?php
17679
17680 namespace Guzzle\Service\Description;
17681
17682 use Guzzle\Common\Exception\InvalidArgumentException;
17683
17684 /**
17685 * Data object holding the information of an API command
17686 */
17687 class Operation implements OperationInterface
17688 {
17689 /** @var string Default command class to use when none is specified */
17690 const DEFAULT_COMMAND_CLASS = 'Guzzle\\Service\\Command\\OperationCommand';
17691
17692 /** @var array Hashmap of properties that can be specified. Represented as a hash to speed up constructor. */
17693 protected static $properties = array(
17694 'name' => true, 'httpMethod' => true, 'uri' => true, 'class' => true, 'responseClass' => true,
17695 'responseType' => true, 'responseNotes' => true, 'notes' => true, 'summary' => true, 'documentationUrl' => true,
17696 'deprecated' => true, 'data' => true, 'parameters' => true, 'additionalParameters' => true,
17697 'errorResponses' => true
17698 );
17699
17700 /** @var array Parameters */
17701 protected $parameters = array();
17702
17703 /** @var Parameter Additional parameters schema */
17704 protected $additionalParameters;
17705
17706 /** @var string Name of the command */
17707 protected $name;
17708
17709 /** @var string HTTP method */
17710 protected $httpMethod;
17711
17712 /** @var string This is a short summary of what the operation does */
17713 protected $summary;
17714
17715 /** @var string A longer text field to explain the behavior of the operation. */
17716 protected $notes;
17717
17718 /** @var string Reference URL providing more information about the operation */
17719 protected $documentationUrl;
17720
17721 /** @var string HTTP URI of the command */
17722 protected $uri;
17723
17724 /** @var string Class of the command object */
17725 protected $class;
17726
17727 /** @var string This is what is returned from the method */
17728 protected $responseClass;
17729
17730 /** @var string Type information about the response */
17731 protected $responseType;
17732
17733 /** @var string Information about the response returned by the operation */
17734 protected $responseNotes;
17735
17736 /** @var bool Whether or not the command is deprecated */
17737 protected $deprecated;
17738
17739 /** @var array Array of errors that could occur when running the command */
17740 protected $errorResponses;
17741
17742 /** @var ServiceDescriptionInterface */
17743 protected $description;
17744
17745 /** @var array Extra operation information */
17746 protected $data;
17747
17748 /**
17749 * Builds an Operation object using an array of configuration data:
17750 * - name: (string) Name of the command
17751 * - httpMethod: (string) HTTP method of the operation
17752 * - uri: (string) URI template that can create a relative or absolute URL
17753 * - class: (string) Concrete class that implements this command
17754 * - parameters: (array) Associative array of parameters for the command. {@see Parameter} for information.
17755 * - summary: (string) This is a short summary of what the operation does
17756 * - notes: (string) A longer text field to explain the behavior of the operation.
17757 * - documentationUrl: (string) Reference URL providing more information about the operation
17758 * - responseClass: (string) This is what is returned from the method. Can be a primitive, PSR-0 compliant
17759 * class name, or model.
17760 * - responseNotes: (string) Information about the response returned by the operation
17761 * - responseType: (string) One of 'primitive', 'class', 'model', or 'documentation'. If not specified, this
17762 * value will be automatically inferred based on whether or not there is a model matching the
17763 * name, if a matching PSR-0 compliant class name is found, or set to 'primitive' by default.
17764 * - deprecated: (bool) Set to true if this is a deprecated command
17765 * - errorResponses: (array) Errors that could occur when executing the command. Array of hashes, each with a
17766 * 'code' (the HTTP response code), 'phrase' (response reason phrase or description of the
17767 * error), and 'class' (a custom exception class that would be thrown if the error is
17768 * encountered).
17769 * - data: (array) Any extra data that might be used to help build or serialize the operation
17770 * - additionalParameters: (null|array) Parameter schema to use when an option is passed to the operation that is
17771 * not in the schema
17772 *
17773 * @param array $config Array of configuration data
17774 * @param ServiceDescriptionInterface $description Service description used to resolve models if $ref tags are found
17775 */
17776 public function __construct(array $config = array(), ServiceDescriptionInterface $description = null)
17777 {
17778 $this->description = $description;
17779
17780 // Get the intersection of the available properties and properties set on the operation
17781 foreach (array_intersect_key($config, self::$properties) as $key => $value) {
17782 $this->{$key} = $value;
17783 }
17784
17785 $this->class = $this->class ?: self::DEFAULT_COMMAND_CLASS;
17786 $this->deprecated = (bool) $this->deprecated;
17787 $this->errorResponses = $this->errorResponses ?: array();
17788 $this->data = $this->data ?: array();
17789
17790 if (!$this->responseClass) {
17791 $this->responseClass = 'array';
17792 $this->responseType = 'primitive';
17793 } elseif ($this->responseType) {
17794 // Set the response type to perform validation
17795 $this->setResponseType($this->responseType);
17796 } else {
17797 // A response class was set and no response type was set, so guess what the type is
17798 $this->inferResponseType();
17799 }
17800
17801 // Parameters need special handling when adding
17802 if ($this->parameters) {
17803 foreach ($this->parameters as $name => $param) {
17804 if ($param instanceof Parameter) {
17805 $param->setName($name)->setParent($this);
17806 } elseif (is_array($param)) {
17807 $param['name'] = $name;
17808 $this->addParam(new Parameter($param, $this->description));
17809 }
17810 }
17811 }
17812
17813 if ($this->additionalParameters) {
17814 if ($this->additionalParameters instanceof Parameter) {
17815 $this->additionalParameters->setParent($this);
17816 } elseif (is_array($this->additionalParameters)) {
17817 $this->setadditionalParameters(new Parameter($this->additionalParameters, $this->description));
17818 }
17819 }
17820 }
17821
17822 public function toArray()
17823 {
17824 $result = array();
17825 // Grab valid properties and filter out values that weren't set
17826 foreach (array_keys(self::$properties) as $check) {
17827 if ($value = $this->{$check}) {
17828 $result[$check] = $value;
17829 }
17830 }
17831 // Remove the name property
17832 unset($result['name']);
17833 // Parameters need to be converted to arrays
17834 $result['parameters'] = array();
17835 foreach ($this->parameters as $key => $param) {
17836 $result['parameters'][$key] = $param->toArray();
17837 }
17838 // Additional parameters need to be cast to an array
17839 if ($this->additionalParameters instanceof Parameter) {
17840 $result['additionalParameters'] = $this->additionalParameters->toArray();
17841 }
17842
17843 return $result;
17844 }
17845
17846 public function getServiceDescription()
17847 {
17848 return $this->description;
17849 }
17850
17851 public function setServiceDescription(ServiceDescriptionInterface $description)
17852 {
17853 $this->description = $description;
17854
17855 return $this;
17856 }
17857
17858 public function getParams()
17859 {
17860 return $this->parameters;
17861 }
17862
17863 public function getParamNames()
17864 {
17865 return array_keys($this->parameters);
17866 }
17867
17868 public function hasParam($name)
17869 {
17870 return isset($this->parameters[$name]);
17871 }
17872
17873 public function getParam($param)
17874 {
17875 return isset($this->parameters[$param]) ? $this->parameters[$param] : null;
17876 }
17877
17878 /**
17879 * Add a parameter to the command
17880 *
17881 * @param Parameter $param Parameter to add
17882 *
17883 * @return self
17884 */
17885 public function addParam(Parameter $param)
17886 {
17887 $this->parameters[$param->getName()] = $param;
17888 $param->setParent($this);
17889
17890 return $this;
17891 }
17892
17893 /**
17894 * Remove a parameter from the command
17895 *
17896 * @param string $name Name of the parameter to remove
17897 *
17898 * @return self
17899 */
17900 public function removeParam($name)
17901 {
17902 unset($this->parameters[$name]);
17903
17904 return $this;
17905 }
17906
17907 public function getHttpMethod()
17908 {
17909 return $this->httpMethod;
17910 }
17911
17912 /**
17913 * Set the HTTP method of the command
17914 *
17915 * @param string $httpMethod Method to set
17916 *
17917 * @return self
17918 */
17919 public function setHttpMethod($httpMethod)
17920 {
17921 $this->httpMethod = $httpMethod;
17922
17923 return $this;
17924 }
17925
17926 public function getClass()
17927 {
17928 return $this->class;
17929 }
17930
17931 /**
17932 * Set the concrete class of the command
17933 *
17934 * @param string $className Concrete class name
17935 *
17936 * @return self
17937 */
17938 public function setClass($className)
17939 {
17940 $this->class = $className;
17941
17942 return $this;
17943 }
17944
17945 public function getName()
17946 {
17947 return $this->name;
17948 }
17949
17950 /**
17951 * Set the name of the command
17952 *
17953 * @param string $name Name of the command
17954 *
17955 * @return self
17956 */
17957 public function setName($name)
17958 {
17959 $this->name = $name;
17960
17961 return $this;
17962 }
17963
17964 public function getSummary()
17965 {
17966 return $this->summary;
17967 }
17968
17969 /**
17970 * Set a short summary of what the operation does
17971 *
17972 * @param string $summary Short summary of the operation
17973 *
17974 * @return self
17975 */
17976 public function setSummary($summary)
17977 {
17978 $this->summary = $summary;
17979
17980 return $this;
17981 }
17982
17983 public function getNotes()
17984 {
17985 return $this->notes;
17986 }
17987
17988 /**
17989 * Set a longer text field to explain the behavior of the operation.
17990 *
17991 * @param string $notes Notes on the operation
17992 *
17993 * @return self
17994 */
17995 public function setNotes($notes)
17996 {
17997 $this->notes = $notes;
17998
17999 return $this;
18000 }
18001
18002 public function getDocumentationUrl()
18003 {
18004 return $this->documentationUrl;
18005 }
18006
18007 /**
18008 * Set the URL pointing to additional documentation on the command
18009 *
18010 * @param string $docUrl Documentation URL
18011 *
18012 * @return self
18013 */
18014 public function setDocumentationUrl($docUrl)
18015 {
18016 $this->documentationUrl = $docUrl;
18017
18018 return $this;
18019 }
18020
18021 public function getResponseClass()
18022 {
18023 return $this->responseClass;
18024 }
18025
18026 /**
18027 * Set what is returned from the method. Can be a primitive, class name, or model. For example: 'array',
18028 * 'Guzzle\\Foo\\Baz', or 'MyModelName' (to reference a model by ID).
18029 *
18030 * @param string $responseClass Type of response
18031 *
18032 * @return self
18033 */
18034 public function setResponseClass($responseClass)
18035 {
18036 $this->responseClass = $responseClass;
18037 $this->inferResponseType();
18038
18039 return $this;
18040 }
18041
18042 public function getResponseType()
18043 {
18044 return $this->responseType;
18045 }
18046
18047 /**
18048 * Set qualifying information about the responseClass. One of 'primitive', 'class', 'model', or 'documentation'
18049 *
18050 * @param string $responseType Response type information
18051 *
18052 * @return self
18053 * @throws InvalidArgumentException
18054 */
18055 public function setResponseType($responseType)
18056 {
18057 static $types = array(
18058 self::TYPE_PRIMITIVE => true,
18059 self::TYPE_CLASS => true,
18060 self::TYPE_MODEL => true,
18061 self::TYPE_DOCUMENTATION => true
18062 );
18063 if (!isset($types[$responseType])) {
18064 throw new InvalidArgumentException('responseType must be one of ' . implode(', ', array_keys($types)));
18065 }
18066
18067 $this->responseType = $responseType;
18068
18069 return $this;
18070 }
18071
18072 public function getResponseNotes()
18073 {
18074 return $this->responseNotes;
18075 }
18076
18077 /**
18078 * Set notes about the response of the operation
18079 *
18080 * @param string $notes Response notes
18081 *
18082 * @return self
18083 */
18084 public function setResponseNotes($notes)
18085 {
18086 $this->responseNotes = $notes;
18087
18088 return $this;
18089 }
18090
18091 public function getDeprecated()
18092 {
18093 return $this->deprecated;
18094 }
18095
18096 /**
18097 * Set whether or not the command is deprecated
18098 *
18099 * @param bool $isDeprecated Set to true to mark as deprecated
18100 *
18101 * @return self
18102 */
18103 public function setDeprecated($isDeprecated)
18104 {
18105 $this->deprecated = $isDeprecated;
18106
18107 return $this;
18108 }
18109
18110 public function getUri()
18111 {
18112 return $this->uri;
18113 }
18114
18115 /**
18116 * Set the URI template of the command
18117 *
18118 * @param string $uri URI template to set
18119 *
18120 * @return self
18121 */
18122 public function setUri($uri)
18123 {
18124 $this->uri = $uri;
18125
18126 return $this;
18127 }
18128
18129 public function getErrorResponses()
18130 {
18131 return $this->errorResponses;
18132 }
18133
18134 /**
18135 * Add an error to the command
18136 *
18137 * @param string $code HTTP response code
18138 * @param string $reason HTTP response reason phrase or information about the error
18139 * @param string $class Exception class associated with the error
18140 *
18141 * @return self
18142 */
18143 public function addErrorResponse($code, $reason, $class)
18144 {
18145 $this->errorResponses[] = array('code' => $code, 'reason' => $reason, 'class' => $class);
18146
18147 return $this;
18148 }
18149
18150 /**
18151 * Set all of the error responses of the operation
18152 *
18153 * @param array $errorResponses Hash of error name to a hash containing a code, reason, class
18154 *
18155 * @return self
18156 */
18157 public function setErrorResponses(array $errorResponses)
18158 {
18159 $this->errorResponses = $errorResponses;
18160
18161 return $this;
18162 }
18163
18164 public function getData($name)
18165 {
18166 return isset($this->data[$name]) ? $this->data[$name] : null;
18167 }
18168
18169 /**
18170 * Set a particular data point on the operation
18171 *
18172 * @param string $name Name of the data value
18173 * @param mixed $value Value to set
18174 *
18175 * @return self
18176 */
18177 public function setData($name, $value)
18178 {
18179 $this->data[$name] = $value;
18180
18181 return $this;
18182 }
18183
18184 /**
18185 * Get the additionalParameters of the operation
18186 *
18187 * @return Paramter|null
18188 */
18189 public function getAdditionalParameters()
18190 {
18191 return $this->additionalParameters;
18192 }
18193
18194 /**
18195 * Set the additionalParameters of the operation
18196 *
18197 * @param Parameter|null $parameter Parameter to set
18198 *
18199 * @return self
18200 */
18201 public function setAdditionalParameters($parameter)
18202 {
18203 if ($this->additionalParameters = $parameter) {
18204 $this->additionalParameters->setParent($this);
18205 }
18206
18207 return $this;
18208 }
18209
18210 /**
18211 * Infer the response type from the responseClass value
18212 */
18213 protected function inferResponseType()
18214 {
18215 static $primitives = array('array' => 1, 'boolean' => 1, 'string' => 1, 'integer' => 1, '' => 1);
18216 if (isset($primitives[$this->responseClass])) {
18217 $this->responseType = self::TYPE_PRIMITIVE;
18218 } elseif ($this->description && $this->description->hasModel($this->responseClass)) {
18219 $this->responseType = self::TYPE_MODEL;
18220 } else {
18221 $this->responseType = self::TYPE_CLASS;
18222 }
18223 }
18224 }
18225 <?php
18226
18227 namespace Guzzle\Service\Description;
18228
18229 use Guzzle\Common\Exception\InvalidArgumentException;
18230 use Guzzle\Common\ToArrayInterface;
18231
18232 /**
18233 * A ServiceDescription stores service information based on a service document
18234 */
18235 class ServiceDescription implements ServiceDescriptionInterface, ToArrayInterface
18236 {
18237 /** @var array Array of {@see OperationInterface} objects */
18238 protected $operations = array();
18239
18240 /** @var array Array of API models */
18241 protected $models = array();
18242
18243 /** @var string Name of the API */
18244 protected $name;
18245
18246 /** @var string API version */
18247 protected $apiVersion;
18248
18249 /** @var string Summary of the API */
18250 protected $description;
18251
18252 /** @var array Any extra API data */
18253 protected $extraData = array();
18254
18255 /** @var ServiceDescriptionLoader Factory used in factory method */
18256 protected static $descriptionLoader;
18257
18258 /** @var string baseUrl/basePath */
18259 protected $baseUrl;
18260
18261 /**
18262 * {@inheritdoc}
18263 * @param string|array $config File to build or array of operation information
18264 * @param array $options Service description factory options
18265 *
18266 * @return self
18267 */
18268 public static function factory($config, array $options = array())
18269 {
18270 // @codeCoverageIgnoreStart
18271 if (!self::$descriptionLoader) {
18272 self::$descriptionLoader = new ServiceDescriptionLoader();
18273 }
18274 // @codeCoverageIgnoreEnd
18275
18276 return self::$descriptionLoader->load($config, $options);
18277 }
18278
18279 /**
18280 * @param array $config Array of configuration data
18281 */
18282 public function __construct(array $config = array())
18283 {
18284 $this->fromArray($config);
18285 }
18286
18287 public function serialize()
18288 {
18289 return json_encode($this->toArray());
18290 }
18291
18292 public function unserialize($json)
18293 {
18294 $this->operations = array();
18295 $this->fromArray(json_decode($json, true));
18296 }
18297
18298 public function toArray()
18299 {
18300 $result = array(
18301 'name' => $this->name,
18302 'apiVersion' => $this->apiVersion,
18303 'baseUrl' => $this->baseUrl,
18304 'description' => $this->description
18305 ) + $this->extraData;
18306 $result['operations'] = array();
18307 foreach ($this->getOperations() as $name => $operation) {
18308 $result['operations'][$operation->getName() ?: $name] = $operation->toArray();
18309 }
18310 if (!empty($this->models)) {
18311 $result['models'] = array();
18312 foreach ($this->models as $id => $model) {
18313 $result['models'][$id] = $model instanceof Parameter ? $model->toArray(): $model;
18314 }
18315 }
18316
18317 return array_filter($result);
18318 }
18319
18320 public function getBaseUrl()
18321 {
18322 return $this->baseUrl;
18323 }
18324
18325 /**
18326 * Set the baseUrl of the description
18327 *
18328 * @param string $baseUrl Base URL of each operation
18329 *
18330 * @return self
18331 */
18332 public function setBaseUrl($baseUrl)
18333 {
18334 $this->baseUrl = $baseUrl;
18335
18336 return $this;
18337 }
18338
18339 public function getOperations()
18340 {
18341 foreach (array_keys($this->operations) as $name) {
18342 $this->getOperation($name);
18343 }
18344
18345 return $this->operations;
18346 }
18347
18348 public function hasOperation($name)
18349 {
18350 return isset($this->operations[$name]);
18351 }
18352
18353 public function getOperation($name)
18354 {
18355 // Lazily retrieve and build operations
18356 if (!isset($this->operations[$name])) {
18357 return null;
18358 }
18359
18360 if (!($this->operations[$name] instanceof Operation)) {
18361 $this->operations[$name] = new Operation($this->operations[$name], $this);
18362 }
18363
18364 return $this->operations[$name];
18365 }
18366
18367 /**
18368 * Add a operation to the service description
18369 *
18370 * @param OperationInterface $operation Operation to add
18371 *
18372 * @return self
18373 */
18374 public function addOperation(OperationInterface $operation)
18375 {
18376 $this->operations[$operation->getName()] = $operation->setServiceDescription($this);
18377
18378 return $this;
18379 }
18380
18381 public function getModel($id)
18382 {
18383 if (!isset($this->models[$id])) {
18384 return null;
18385 }
18386
18387 if (!($this->models[$id] instanceof Parameter)) {
18388 $this->models[$id] = new Parameter($this->models[$id] + array('name' => $id), $this);
18389 }
18390
18391 return $this->models[$id];
18392 }
18393
18394 public function getModels()
18395 {
18396 // Ensure all models are converted into parameter objects
18397 foreach (array_keys($this->models) as $id) {
18398 $this->getModel($id);
18399 }
18400
18401 return $this->models;
18402 }
18403
18404 public function hasModel($id)
18405 {
18406 return isset($this->models[$id]);
18407 }
18408
18409 /**
18410 * Add a model to the service description
18411 *
18412 * @param Parameter $model Model to add
18413 *
18414 * @return self
18415 */
18416 public function addModel(Parameter $model)
18417 {
18418 $this->models[$model->getName()] = $model;
18419
18420 return $this;
18421 }
18422
18423 public function getApiVersion()
18424 {
18425 return $this->apiVersion;
18426 }
18427
18428 public function getName()
18429 {
18430 return $this->name;
18431 }
18432
18433 public function getDescription()
18434 {
18435 return $this->description;
18436 }
18437
18438 public function getData($key)
18439 {
18440 return isset($this->extraData[$key]) ? $this->extraData[$key] : null;
18441 }
18442
18443 public function setData($key, $value)
18444 {
18445 $this->extraData[$key] = $value;
18446
18447 return $this;
18448 }
18449
18450 /**
18451 * Initialize the state from an array
18452 *
18453 * @param array $config Configuration data
18454 * @throws InvalidArgumentException
18455 */
18456 protected function fromArray(array $config)
18457 {
18458 // Keep a list of default keys used in service descriptions that is later used to determine extra data keys
18459 static $defaultKeys = array('name', 'models', 'apiVersion', 'baseUrl', 'description');
18460 // Pull in the default configuration values
18461 foreach ($defaultKeys as $key) {
18462 if (isset($config[$key])) {
18463 $this->{$key} = $config[$key];
18464 }
18465 }
18466
18467 // Account for the Swagger name for Guzzle's baseUrl
18468 if (isset($config['basePath'])) {
18469 $this->baseUrl = $config['basePath'];
18470 }
18471
18472 // Ensure that the models and operations properties are always arrays
18473 $this->models = (array) $this->models;
18474 $this->operations = (array) $this->operations;
18475
18476 // We want to add operations differently than adding the other properties
18477 $defaultKeys[] = 'operations';
18478
18479 // Create operations for each operation
18480 if (isset($config['operations'])) {
18481 foreach ($config['operations'] as $name => $operation) {
18482 if (!($operation instanceof Operation) && !is_array($operation)) {
18483 throw new InvalidArgumentException('Invalid operation in service description: '
18484 . gettype($operation));
18485 }
18486 $this->operations[$name] = $operation;
18487 }
18488 }
18489
18490 // Get all of the additional properties of the service description and store them in a data array
18491 foreach (array_diff(array_keys($config), $defaultKeys) as $key) {
18492 $this->extraData[$key] = $config[$key];
18493 }
18494 }
18495 }
18496 <?php
18497
18498 namespace Guzzle\Service\Description;
18499
18500 use Guzzle\Common\ToArrayInterface;
18501
18502 /**
18503 * Default parameter validator
18504 */
18505 class SchemaValidator implements ValidatorInterface
18506 {
18507 /** @var self Cache instance of the object */
18508 protected static $instance;
18509
18510 /** @var bool Whether or not integers are converted to strings when an integer is received for a string input */
18511 protected $castIntegerToStringType;
18512
18513 /** @var array Errors encountered while validating */
18514 protected $errors;
18515
18516 /**
18517 * @return self
18518 * @codeCoverageIgnore
18519 */
18520 public static function getInstance()
18521 {
18522 if (!self::$instance) {
18523 self::$instance = new self();
18524 }
18525
18526 return self::$instance;
18527 }
18528
18529 /**
18530 * @param bool $castIntegerToStringType Set to true to convert integers into strings when a required type is a
18531 * string and the input value is an integer. Defaults to true.
18532 */
18533 public function __construct($castIntegerToStringType = true)
18534 {
18535 $this->castIntegerToStringType = $castIntegerToStringType;
18536 }
18537
18538 public function validate(Parameter $param, &$value)
18539 {
18540 $this->errors = array();
18541 $this->recursiveProcess($param, $value);
18542
18543 if (empty($this->errors)) {
18544 return true;
18545 } else {
18546 sort($this->errors);
18547 return false;
18548 }
18549 }
18550
18551 /**
18552 * Get the errors encountered while validating
18553 *
18554 * @return array
18555 */
18556 public function getErrors()
18557 {
18558 return $this->errors ?: array();
18559 }
18560
18561 /**
18562 * Recursively validate a parameter
18563 *
18564 * @param Parameter $param API parameter being validated
18565 * @param mixed $value Value to validate and validate. The value may change during this validate.
18566 * @param string $path Current validation path (used for error reporting)
18567 * @param int $depth Current depth in the validation validate
18568 *
18569 * @return bool Returns true if valid, or false if invalid
18570 */
18571 protected function recursiveProcess(Parameter $param, &$value, $path = '', $depth = 0)
18572 {
18573 // Update the value by adding default or static values
18574 $value = $param->getValue($value);
18575
18576 $required = $param->getRequired();
18577 // if the value is null and the parameter is not required or is static, then skip any further recursion
18578 if ((null === $value && !$required) || $param->getStatic()) {
18579 return true;
18580 }
18581
18582 $type = $param->getType();
18583 // Attempt to limit the number of times is_array is called by tracking if the value is an array
18584 $valueIsArray = is_array($value);
18585 // If a name is set then update the path so that validation messages are more helpful
18586 if ($name = $param->getName()) {
18587 $path .= "[{$name}]";
18588 }
18589
18590 if ($type == 'object') {
18591
18592 // Objects are either associative arrays, ToArrayInterface, or some other object
18593 if ($param->getInstanceOf()) {
18594 $instance = $param->getInstanceOf();
18595 if (!($value instanceof $instance)) {
18596 $this->errors[] = "{$path} must be an instance of {$instance}";
18597 return false;
18598 }
18599 }
18600
18601 // Determine whether or not this "value" has properties and should be traversed
18602 $traverse = $temporaryValue = false;
18603
18604 // Convert the value to an array
18605 if (!$valueIsArray && $value instanceof ToArrayInterface) {
18606 $value = $value->toArray();
18607 }
18608
18609 if ($valueIsArray) {
18610 // Ensure that the array is associative and not numerically indexed
18611 if (isset($value[0])) {
18612 $this->errors[] = "{$path} must be an array of properties. Got a numerically indexed array.";
18613 return false;
18614 }
18615 $traverse = true;
18616 } elseif ($value === null) {
18617 // Attempt to let the contents be built up by default values if possible
18618 $value = array();
18619 $temporaryValue = $valueIsArray = $traverse = true;
18620 }
18621
18622 if ($traverse) {
18623
18624 if ($properties = $param->getProperties()) {
18625 // if properties were found, the validate each property of the value
18626 foreach ($properties as $property) {
18627 $name = $property->getName();
18628 if (isset($value[$name])) {
18629 $this->recursiveProcess($property, $value[$name], $path, $depth + 1);
18630 } else {
18631 $current = null;
18632 $this->recursiveProcess($property, $current, $path, $depth + 1);
18633 // Only set the value if it was populated with something
18634 if ($current) {
18635 $value[$name] = $current;
18636 }
18637 }
18638 }
18639 }
18640
18641 $additional = $param->getAdditionalProperties();
18642 if ($additional !== true) {
18643 // If additional properties were found, then validate each against the additionalProperties attr.
18644 $keys = array_keys($value);
18645 // Determine the keys that were specified that were not listed in the properties of the schema
18646 $diff = array_diff($keys, array_keys($properties));
18647 if (!empty($diff)) {
18648 // Determine which keys are not in the properties
18649 if ($additional instanceOf Parameter) {
18650 foreach ($diff as $key) {
18651 $this->recursiveProcess($additional, $value[$key], "{$path}[{$key}]", $depth);
18652 }
18653 } else {
18654 // if additionalProperties is set to false and there are additionalProperties in the values, then fail
18655 $keys = array_keys($value);
18656 $this->errors[] = sprintf('%s[%s] is not an allowed property', $path, reset($keys));
18657 }
18658 }
18659 }
18660
18661 // A temporary value will be used to traverse elements that have no corresponding input value.
18662 // This allows nested required parameters with default values to bubble up into the input.
18663 // Here we check if we used a temp value and nothing bubbled up, then we need to remote the value.
18664 if ($temporaryValue && empty($value)) {
18665 $value = null;
18666 $valueIsArray = false;
18667 }
18668 }
18669
18670 } elseif ($type == 'array' && $valueIsArray && $param->getItems()) {
18671 foreach ($value as $i => &$item) {
18672 // Validate each item in an array against the items attribute of the schema
18673 $this->recursiveProcess($param->getItems(), $item, $path . "[{$i}]", $depth + 1);
18674 }
18675 }
18676
18677 // If the value is required and the type is not null, then there is an error if the value is not set
18678 if ($required && $value === null && $type != 'null') {
18679 $message = "{$path} is " . ($param->getType() ? ('a required ' . implode(' or ', (array) $param->getType())) : 'required');
18680 if ($param->getDescription()) {
18681 $message .= ': ' . $param->getDescription();
18682 }
18683 $this->errors[] = $message;
18684 return false;
18685 }
18686
18687 // Validate that the type is correct. If the type is string but an integer was passed, the class can be
18688 // instructed to cast the integer to a string to pass validation. This is the default behavior.
18689 if ($type && (!$type = $this->determineType($type, $value))) {
18690 if ($this->castIntegerToStringType && $param->getType() == 'string' && is_integer($value)) {
18691 $value = (string) $value;
18692 } else {
18693 $this->errors[] = "{$path} must be of type " . implode(' or ', (array) $param->getType());
18694 }
18695 }
18696
18697 // Perform type specific validation for strings, arrays, and integers
18698 if ($type == 'string') {
18699
18700 // Strings can have enums which are a list of predefined values
18701 if (($enum = $param->getEnum()) && !in_array($value, $enum)) {
18702 $this->errors[] = "{$path} must be one of " . implode(' or ', array_map(function ($s) {
18703 return '"' . addslashes($s) . '"';
18704 }, $enum));
18705 }
18706 // Strings can have a regex pattern that the value must match
18707 if (($pattern = $param->getPattern()) && !preg_match($pattern, $value)) {
18708 $this->errors[] = "{$path} must match the following regular expression: {$pattern}";
18709 }
18710
18711 $strLen = null;
18712 if ($min = $param->getMinLength()) {
18713 $strLen = strlen($value);
18714 if ($strLen < $min) {
18715 $this->errors[] = "{$path} length must be greater than or equal to {$min}";
18716 }
18717 }
18718 if ($max = $param->getMaxLength()) {
18719 if (($strLen ?: strlen($value)) > $max) {
18720 $this->errors[] = "{$path} length must be less than or equal to {$max}";
18721 }
18722 }
18723
18724 } elseif ($type == 'array') {
18725
18726 $size = null;
18727 if ($min = $param->getMinItems()) {
18728 $size = count($value);
18729 if ($size < $min) {
18730 $this->errors[] = "{$path} must contain {$min} or more elements";
18731 }
18732 }
18733 if ($max = $param->getMaxItems()) {
18734 if (($size ?: count($value)) > $max) {
18735 $this->errors[] = "{$path} must contain {$max} or fewer elements";
18736 }
18737 }
18738
18739 } elseif ($type == 'integer' || $type == 'number' || $type == 'numeric') {
18740 if (($min = $param->getMinimum()) && $value < $min) {
18741 $this->errors[] = "{$path} must be greater than or equal to {$min}";
18742 }
18743 if (($max = $param->getMaximum()) && $value > $max) {
18744 $this->errors[] = "{$path} must be less than or equal to {$max}";
18745 }
18746 }
18747
18748 return empty($this->errors);
18749 }
18750
18751 /**
18752 * From the allowable types, determine the type that the variable matches
18753 *
18754 * @param string $type Parameter type
18755 * @param mixed $value Value to determine the type
18756 *
18757 * @return string|bool Returns the matching type on
18758 */
18759 protected function determineType($type, $value)
18760 {
18761 foreach ((array) $type as $t) {
18762 if ($t == 'string' && (is_string($value) || (is_object($value) && method_exists($value, '__toString')))) {
18763 return 'string';
18764 } elseif ($t == 'object' && (is_array($value) || is_object($value))) {
18765 return 'object';
18766 } elseif ($t == 'array' && is_array($value)) {
18767 return 'array';
18768 } elseif ($t == 'integer' && is_integer($value)) {
18769 return 'integer';
18770 } elseif ($t == 'boolean' && is_bool($value)) {
18771 return 'boolean';
18772 } elseif ($t == 'number' && is_numeric($value)) {
18773 return 'number';
18774 } elseif ($t == 'numeric' && is_numeric($value)) {
18775 return 'numeric';
18776 } elseif ($t == 'null' && !$value) {
18777 return 'null';
18778 } elseif ($t == 'any') {
18779 return 'any';
18780 }
18781 }
18782
18783 return false;
18784 }
18785 }
18786 <?php
18787
18788 namespace Guzzle\Service\Description;
18789
18790 use Guzzle\Common\Exception\InvalidArgumentException;
18791
18792 /**
18793 * JSON Schema formatter class
18794 */
18795 class SchemaFormatter
18796 {
18797 /** @var \DateTimeZone */
18798 protected static $utcTimeZone;
18799
18800 /**
18801 * Format a value by a registered format name
18802 *
18803 * @param string $format Registered format used to format the value
18804 * @param mixed $value Value being formatted
18805 *
18806 * @return mixed
18807 */
18808 public static function format($format, $value)
18809 {
18810 switch ($format) {
18811 case 'date-time':
18812 return self::formatDateTime($value);
18813 case 'date-time-http':
18814 return self::formatDateTimeHttp($value);
18815 case 'date':
18816 return self::formatDate($value);
18817 case 'time':
18818 return self::formatTime($value);
18819 case 'timestamp':
18820 return self::formatTimestamp($value);
18821 case 'boolean-string':
18822 return self::formatBooleanAsString($value);
18823 default:
18824 return $value;
18825 }
18826 }
18827
18828 /**
18829 * Create a ISO 8601 (YYYY-MM-DDThh:mm:ssZ) formatted date time value in UTC time
18830 *
18831 * @param string|integer|\DateTime $value Date time value
18832 *
18833 * @return string
18834 */
18835 public static function formatDateTime($value)
18836 {
18837 return self::dateFormatter($value, 'Y-m-d\TH:i:s\Z');
18838 }
18839
18840 /**
18841 * Create an HTTP date (RFC 1123 / RFC 822) formatted UTC date-time string
18842 *
18843 * @param string|integer|\DateTime $value Date time value
18844 *
18845 * @return string
18846 */
18847 public static function formatDateTimeHttp($value)
18848 {
18849 return self::dateFormatter($value, 'D, d M Y H:i:s \G\M\T');
18850 }
18851
18852 /**
18853 * Create a YYYY-MM-DD formatted string
18854 *
18855 * @param string|integer|\DateTime $value Date time value
18856 *
18857 * @return string
18858 */
18859 public static function formatDate($value)
18860 {
18861 return self::dateFormatter($value, 'Y-m-d');
18862 }
18863
18864 /**
18865 * Create a hh:mm:ss formatted string
18866 *
18867 * @param string|integer|\DateTime $value Date time value
18868 *
18869 * @return string
18870 */
18871 public static function formatTime($value)
18872 {
18873 return self::dateFormatter($value, 'H:i:s');
18874 }
18875
18876 /**
18877 * Formats a boolean value as a string
18878 *
18879 * @param string|integer|bool $value Value to convert to a boolean 'true' / 'false' value
18880 *
18881 * @return string
18882 */
18883 public static function formatBooleanAsString($value)
18884 {
18885 return filter_var($value, FILTER_VALIDATE_BOOLEAN) ? 'true' : 'false';
18886 }
18887
18888 /**
18889 * Return a UNIX timestamp in the UTC timezone
18890 *
18891 * @param string|integer|\DateTime $value Time value
18892 *
18893 * @return int
18894 */
18895 public static function formatTimestamp($value)
18896 {
18897 return self::dateFormatter($value, 'U');
18898 }
18899
18900 /**
18901 * Get a UTC DateTimeZone object
18902 *
18903 * @return \DateTimeZone
18904 */
18905 protected static function getUtcTimeZone()
18906 {
18907 // @codeCoverageIgnoreStart
18908 if (!self::$utcTimeZone) {
18909 self::$utcTimeZone = new \DateTimeZone('UTC');
18910 }
18911 // @codeCoverageIgnoreEnd
18912
18913 return self::$utcTimeZone;
18914 }
18915
18916 /**
18917 * Perform the actual DateTime formatting
18918 *
18919 * @param int|string|\DateTime $dateTime Date time value
18920 * @param string $format Format of the result
18921 *
18922 * @return string
18923 * @throws InvalidArgumentException
18924 */
18925 protected static function dateFormatter($dateTime, $format)
18926 {
18927 if (is_numeric($dateTime)) {
18928 return gmdate($format, (int) $dateTime);
18929 }
18930
18931 if (is_string($dateTime)) {
18932 $dateTime = new \DateTime($dateTime);
18933 }
18934
18935 if ($dateTime instanceof \DateTime) {
18936 return $dateTime->setTimezone(self::getUtcTimeZone())->format($format);
18937 }
18938
18939 throw new InvalidArgumentException('Date/Time values must be either a string, integer, or DateTime object');
18940 }
18941 }
18942 <?php
18943
18944 namespace Guzzle\Service\Description;
18945
18946 use Guzzle\Common\ToArrayInterface;
18947
18948 /**
18949 * Interface defining data objects that hold the information of an API operation
18950 */
18951 interface OperationInterface extends ToArrayInterface
18952 {
18953 const TYPE_PRIMITIVE = 'primitive';
18954 const TYPE_CLASS = 'class';
18955 const TYPE_DOCUMENTATION = 'documentation';
18956 const TYPE_MODEL = 'model';
18957
18958 /**
18959 * Get the service description that the operation belongs to
18960 *
18961 * @return ServiceDescriptionInterface|null
18962 */
18963 public function getServiceDescription();
18964
18965 /**
18966 * Set the service description that the operation belongs to
18967 *
18968 * @param ServiceDescriptionInterface $description Service description
18969 *
18970 * @return self
18971 */
18972 public function setServiceDescription(ServiceDescriptionInterface $description);
18973
18974 /**
18975 * Get the params of the operation
18976 *
18977 * @return array
18978 */
18979 public function getParams();
18980
18981 /**
18982 * Returns an array of parameter names
18983 *
18984 * @return array
18985 */
18986 public function getParamNames();
18987
18988 /**
18989 * Check if the operation has a specific parameter by name
18990 *
18991 * @param string $name Name of the param
18992 *
18993 * @return bool
18994 */
18995 public function hasParam($name);
18996
18997 /**
18998 * Get a single parameter of the operation
18999 *
19000 * @param string $param Parameter to retrieve by name
19001 *
19002 * @return Parameter|null
19003 */
19004 public function getParam($param);
19005
19006 /**
19007 * Get the HTTP method of the operation
19008 *
19009 * @return string|null
19010 */
19011 public function getHttpMethod();
19012
19013 /**
19014 * Get the concrete operation class that implements this operation
19015 *
19016 * @return string
19017 */
19018 public function getClass();
19019
19020 /**
19021 * Get the name of the operation
19022 *
19023 * @return string|null
19024 */
19025 public function getName();
19026
19027 /**
19028 * Get a short summary of what the operation does
19029 *
19030 * @return string|null
19031 */
19032 public function getSummary();
19033
19034 /**
19035 * Get a longer text field to explain the behavior of the operation
19036 *
19037 * @return string|null
19038 */
19039 public function getNotes();
19040
19041 /**
19042 * Get the documentation URL of the operation
19043 *
19044 * @return string|null
19045 */
19046 public function getDocumentationUrl();
19047
19048 /**
19049 * Get what is returned from the method. Can be a primitive, class name, or model. For example, the responseClass
19050 * could be 'array', which would inherently use a responseType of 'primitive'. Using a class name would set a
19051 * responseType of 'class'. Specifying a model by ID will use a responseType of 'model'.
19052 *
19053 * @return string|null
19054 */
19055 public function getResponseClass();
19056
19057 /**
19058 * Get information about how the response is unmarshalled: One of 'primitive', 'class', 'model', or 'documentation'
19059 *
19060 * @return string
19061 */
19062 public function getResponseType();
19063
19064 /**
19065 * Get notes about the response of the operation
19066 *
19067 * @return string|null
19068 */
19069 public function getResponseNotes();
19070
19071 /**
19072 * Get whether or not the operation is deprecated
19073 *
19074 * @return bool
19075 */
19076 public function getDeprecated();
19077
19078 /**
19079 * Get the URI that will be merged into the generated request
19080 *
19081 * @return string
19082 */
19083 public function getUri();
19084
19085 /**
19086 * Get the errors that could be encountered when executing the operation
19087 *
19088 * @return array
19089 */
19090 public function getErrorResponses();
19091
19092 /**
19093 * Get extra data from the operation
19094 *
19095 * @param string $name Name of the data point to retrieve
19096 *
19097 * @return mixed|null
19098 */
19099 public function getData($name);
19100 }
19101 <?php
19102
19103 namespace Guzzle\Service\Description;
19104
19105 /**
19106 * A ServiceDescription stores service information based on a service document
19107 */
19108 interface ServiceDescriptionInterface extends \Serializable
19109 {
19110 /**
19111 * Get the basePath/baseUrl of the description
19112 *
19113 * @return string
19114 */
19115 public function getBaseUrl();
19116
19117 /**
19118 * Get the API operations of the service
19119 *
19120 * @return array Returns an array of {@see OperationInterface} objects
19121 */
19122 public function getOperations();
19123
19124 /**
19125 * Check if the service has an operation by name
19126 *
19127 * @param string $name Name of the operation to check
19128 *
19129 * @return bool
19130 */
19131 public function hasOperation($name);
19132
19133 /**
19134 * Get an API operation by name
19135 *
19136 * @param string $name Name of the command
19137 *
19138 * @return OperationInterface|null
19139 */
19140 public function getOperation($name);
19141
19142 /**
19143 * Get a specific model from the description
19144 *
19145 * @param string $id ID of the model
19146 *
19147 * @return Parameter|null
19148 */
19149 public function getModel($id);
19150
19151 /**
19152 * Get all service description models
19153 *
19154 * @return array
19155 */
19156 public function getModels();
19157
19158 /**
19159 * Check if the description has a specific model by name
19160 *
19161 * @param string $id ID of the model
19162 *
19163 * @return bool
19164 */
19165 public function hasModel($id);
19166
19167 /**
19168 * Get the API version of the service
19169 *
19170 * @return string
19171 */
19172 public function getApiVersion();
19173
19174 /**
19175 * Get the name of the API
19176 *
19177 * @return string
19178 */
19179 public function getName();
19180
19181 /**
19182 * Get a summary of the purpose of the API
19183 *
19184 * @return string
19185 */
19186 public function getDescription();
19187
19188 /**
19189 * Get arbitrary data from the service description that is not part of the Guzzle spec
19190 *
19191 * @param string $key Data key to retrieve
19192 *
19193 * @return null|mixed
19194 */
19195 public function getData($key);
19196
19197 /**
19198 * Set arbitrary data on the service description
19199 *
19200 * @param string $key Data key to set
19201 * @param mixed $value Value to set
19202 *
19203 * @return self
19204 */
19205 public function setData($key, $value);
19206 }
19207 <?php
19208
19209 namespace Guzzle\Service;
19210
19211 use Guzzle\Common\FromConfigInterface;
19212 use Guzzle\Common\Exception\InvalidArgumentException;
19213 use Guzzle\Http\ClientInterface as HttpClientInterface;
19214 use Guzzle\Service\Exception\CommandTransferException;
19215 use Guzzle\Service\Command\CommandInterface;
19216 use Guzzle\Service\Description\ServiceDescriptionInterface;
19217 use Guzzle\Service\Resource\ResourceIteratorInterface;
19218
19219 /**
19220 * Client interface for executing commands on a web service.
19221 */
19222 interface ClientInterface extends HttpClientInterface, FromConfigInterface
19223 {
19224 /**
19225 * Get a command by name. First, the client will see if it has a service description and if the service description
19226 * defines a command by the supplied name. If no dynamic command is found, the client will look for a concrete
19227 * command class exists matching the name supplied. If neither are found, an InvalidArgumentException is thrown.
19228 *
19229 * @param string $name Name of the command to retrieve
19230 * @param array $args Arguments to pass to the command
19231 *
19232 * @return CommandInterface
19233 * @throws InvalidArgumentException if no command can be found by name
19234 */
19235 public function getCommand($name, array $args = array());
19236
19237 /**
19238 * Execute one or more commands
19239 *
19240 * @param CommandInterface|array|Traversable $command Command, array of commands or Traversable object containing commands to execute
19241 *
19242 * @return mixed Returns the result of the executed command or an array of commands if executing multiple commands
19243 * @throws InvalidArgumentException if an invalid command is passed
19244 * @throws CommandTransferException if an exception is encountered when transferring multiple commands
19245 */
19246 public function execute($command);
19247
19248 /**
19249 * Set the service description of the client
19250 *
19251 * @param ServiceDescriptionInterface $service Service description
19252 *
19253 * @return ClientInterface
19254 */
19255 public function setDescription(ServiceDescriptionInterface $service);
19256
19257 /**
19258 * Get the service description of the client
19259 *
19260 * @return ServiceDescriptionInterface|null
19261 */
19262 public function getDescription();
19263
19264 /**
19265 * Get a resource iterator from the client.
19266 *
19267 * @param string|CommandInterface $command Command class or command name.
19268 * @param array $commandOptions Command options used when creating commands.
19269 * @param array $iteratorOptions Iterator options passed to the iterator when it is instantiated.
19270 *
19271 * @return ResourceIteratorInterface
19272 */
19273 public function getIterator($command, array $commandOptions = null, array $iteratorOptions = array());
19274 }
19275 <?php
19276
19277 namespace Guzzle\Service;
19278
19279 use Guzzle\Common\Collection;
19280 use Guzzle\Common\Exception\InvalidArgumentException;
19281 use Guzzle\Common\Exception\BadMethodCallException;
19282 use Guzzle\Common\Version;
19283 use Guzzle\Inflection\InflectorInterface;
19284 use Guzzle\Inflection\Inflector;
19285 use Guzzle\Http\Client as HttpClient;
19286 use Guzzle\Http\Exception\MultiTransferException;
19287 use Guzzle\Service\Exception\CommandTransferException;
19288 use Guzzle\Http\Message\RequestInterface;
19289 use Guzzle\Service\Command\CommandInterface;
19290 use Guzzle\Service\Command\Factory\CompositeFactory;
19291 use Guzzle\Service\Command\Factory\FactoryInterface as CommandFactoryInterface;
19292 use Guzzle\Service\Resource\ResourceIteratorClassFactory;
19293 use Guzzle\Service\Resource\ResourceIteratorFactoryInterface;
19294 use Guzzle\Service\Description\ServiceDescriptionInterface;
19295
19296 /**
19297 * Client object for executing commands on a web service.
19298 */
19299 class Client extends HttpClient implements ClientInterface
19300 {
19301 const COMMAND_PARAMS = 'command.params';
19302
19303 /** @var ServiceDescriptionInterface Description of the service and possible commands */
19304 protected $serviceDescription;
19305
19306 /** @var CommandFactoryInterface */
19307 protected $commandFactory;
19308
19309 /** @var ResourceIteratorFactoryInterface */
19310 protected $resourceIteratorFactory;
19311
19312 /** @var InflectorInterface Inflector associated with the service/client */
19313 protected $inflector;
19314
19315 /**
19316 * Basic factory method to create a new client. Extend this method in subclasses to build more complex clients.
19317 *
19318 * @param array|Collection $config Configuration data
19319 *
19320 * @return Client
19321 */
19322 public static function factory($config = array())
19323 {
19324 return new static(isset($config['base_url']) ? $config['base_url'] : null, $config);
19325 }
19326
19327 public static function getAllEvents()
19328 {
19329 return array_merge(HttpClient::getAllEvents(), array(
19330 'client.command.create',
19331 'command.before_prepare',
19332 'command.after_prepare',
19333 'command.before_send',
19334 'command.after_send',
19335 'command.parse_response'
19336 ));
19337 }
19338
19339 /**
19340 * Magic method used to retrieve a command
19341 *
19342 * @param string $method Name of the command object to instantiate
19343 * @param array $args Arguments to pass to the command
19344 *
19345 * @return mixed Returns the result of the command
19346 * @throws BadMethodCallException when a command is not found
19347 */
19348 public function __call($method, $args)
19349 {
19350 return $this->getCommand($method, isset($args[0]) ? $args[0] : array())->getResult();
19351 }
19352
19353 public function getCommand($name, array $args = array())
19354 {
19355 // Add global client options to the command
19356 if ($options = $this->getConfig(self::COMMAND_PARAMS)) {
19357 $args += $options;
19358 }
19359
19360 if (!($command = $this->getCommandFactory()->factory($name, $args))) {
19361 throw new InvalidArgumentException("Command was not found matching {$name}");
19362 }
19363
19364 $command->setClient($this);
19365 $this->dispatch('client.command.create', array('client' => $this, 'command' => $command));
19366
19367 return $command;
19368 }
19369
19370 /**
19371 * Set the command factory used to create commands by name
19372 *
19373 * @param CommandFactoryInterface $factory Command factory
19374 *
19375 * @return self
19376 */
19377 public function setCommandFactory(CommandFactoryInterface $factory)
19378 {
19379 $this->commandFactory = $factory;
19380
19381 return $this;
19382 }
19383
19384 /**
19385 * Set the resource iterator factory associated with the client
19386 *
19387 * @param ResourceIteratorFactoryInterface $factory Resource iterator factory
19388 *
19389 * @return self
19390 */
19391 public function setResourceIteratorFactory(ResourceIteratorFactoryInterface $factory)
19392 {
19393 $this->resourceIteratorFactory = $factory;
19394
19395 return $this;
19396 }
19397
19398 public function getIterator($command, array $commandOptions = null, array $iteratorOptions = array())
19399 {
19400 if (!($command instanceof CommandInterface)) {
19401 $command = $this->getCommand($command, $commandOptions ?: array());
19402 }
19403
19404 return $this->getResourceIteratorFactory()->build($command, $iteratorOptions);
19405 }
19406
19407 public function execute($command)
19408 {
19409 if ($command instanceof CommandInterface) {
19410 $this->send($this->prepareCommand($command));
19411 $this->dispatch('command.after_send', array('command' => $command));
19412 return $command->getResult();
19413 } elseif (is_array($command) || $command instanceof \Traversable) {
19414 return $this->executeMultiple($command);
19415 } else {
19416 throw new InvalidArgumentException('Command must be a command or array of commands');
19417 }
19418 }
19419
19420 public function setDescription(ServiceDescriptionInterface $service)
19421 {
19422 $this->serviceDescription = $service;
19423
19424 // If a baseUrl was set on the description, then update the client
19425 if ($baseUrl = $service->getBaseUrl()) {
19426 $this->setBaseUrl($baseUrl);
19427 }
19428
19429 return $this;
19430 }
19431
19432 public function getDescription()
19433 {
19434 return $this->serviceDescription;
19435 }
19436
19437 /**
19438 * Set the inflector used with the client
19439 *
19440 * @param InflectorInterface $inflector Inflection object
19441 *
19442 * @return self
19443 */
19444 public function setInflector(InflectorInterface $inflector)
19445 {
19446 $this->inflector = $inflector;
19447
19448 return $this;
19449 }
19450
19451 /**
19452 * Get the inflector used with the client
19453 *
19454 * @return self
19455 */
19456 public function getInflector()
19457 {
19458 if (!$this->inflector) {
19459 $this->inflector = Inflector::getDefault();
19460 }
19461
19462 return $this->inflector;
19463 }
19464
19465 /**
19466 * Prepare a command for sending and get the RequestInterface object created by the command
19467 *
19468 * @param CommandInterface $command Command to prepare
19469 *
19470 * @return RequestInterface
19471 */
19472 protected function prepareCommand(CommandInterface $command)
19473 {
19474 // Set the client and prepare the command
19475 $request = $command->setClient($this)->prepare();
19476 // Set the state to new if the command was previously executed
19477 $request->setState(RequestInterface::STATE_NEW);
19478 $this->dispatch('command.before_send', array('command' => $command));
19479
19480 return $request;
19481 }
19482
19483 /**
19484 * Execute multiple commands in parallel
19485 *
19486 * @param array|Traversable $commands Array of CommandInterface objects to execute
19487 *
19488 * @return array Returns an array of the executed commands
19489 * @throws Exception\CommandTransferException
19490 */
19491 protected function executeMultiple($commands)
19492 {
19493 $requests = array();
19494 $commandRequests = new \SplObjectStorage();
19495
19496 foreach ($commands as $command) {
19497 $request = $this->prepareCommand($command);
19498 $commandRequests[$request] = $command;
19499 $requests[] = $request;
19500 }
19501
19502 try {
19503 $this->send($requests);
19504 foreach ($commands as $command) {
19505 $this->dispatch('command.after_send', array('command' => $command));
19506 }
19507 return $commands;
19508 } catch (MultiTransferException $failureException) {
19509 // Throw a CommandTransferException using the successful and failed commands
19510 $e = CommandTransferException::fromMultiTransferException($failureException);
19511
19512 // Remove failed requests from the successful requests array and add to the failures array
19513 foreach ($failureException->getFailedRequests() as $request) {
19514 if (isset($commandRequests[$request])) {
19515 $e->addFailedCommand($commandRequests[$request]);
19516 unset($commandRequests[$request]);
19517 }
19518 }
19519
19520 // Always emit the command after_send events for successful commands
19521 foreach ($commandRequests as $success) {
19522 $e->addSuccessfulCommand($commandRequests[$success]);
19523 $this->dispatch('command.after_send', array('command' => $commandRequests[$success]));
19524 }
19525
19526 throw $e;
19527 }
19528 }
19529
19530 protected function getResourceIteratorFactory()
19531 {
19532 if (!$this->resourceIteratorFactory) {
19533 // Build the default resource iterator factory if one is not set
19534 $clientClass = get_class($this);
19535 $prefix = substr($clientClass, 0, strrpos($clientClass, '\\'));
19536 $this->resourceIteratorFactory = new ResourceIteratorClassFactory(array(
19537 "{$prefix}\\Iterator",
19538 "{$prefix}\\Model"
19539 ));
19540 }
19541
19542 return $this->resourceIteratorFactory;
19543 }
19544
19545 /**
19546 * Get the command factory associated with the client
19547 *
19548 * @return CommandFactoryInterface
19549 */
19550 protected function getCommandFactory()
19551 {
19552 if (!$this->commandFactory) {
19553 $this->commandFactory = CompositeFactory::getDefaultChain($this);
19554 }
19555
19556 return $this->commandFactory;
19557 }
19558
19559 /**
19560 * @deprecated
19561 * @codeCoverageIgnore
19562 */
19563 public function enableMagicMethods($isEnabled)
19564 {
19565 Version::warn(__METHOD__ . ' is deprecated');
19566 }
19567 }
19568 <?php
19569
19570 namespace Guzzle\Service;
19571
19572 use Guzzle\Cache\CacheAdapterInterface;
19573
19574 /**
19575 * Decorator that adds caching to a service description loader
19576 */
19577 class CachingConfigLoader implements ConfigLoaderInterface
19578 {
19579 /** @var ConfigLoaderInterface */
19580 protected $loader;
19581
19582 /** @var CacheAdapterInterface */
19583 protected $cache;
19584
19585 /**
19586 * @param ConfigLoaderInterface $loader Loader used to load the config when there is a cache miss
19587 * @param CacheAdapterInterface $cache Object used to cache the loaded result
19588 */
19589 public function __construct(ConfigLoaderInterface $loader, CacheAdapterInterface $cache)
19590 {
19591 $this->loader = $loader;
19592 $this->cache = $cache;
19593 }
19594
19595 public function load($config, array $options = array())
19596 {
19597 if (!is_string($config)) {
19598 $key = false;
19599 } else {
19600 $key = 'loader_' . crc32($config);
19601 if ($result = $this->cache->fetch($key)) {
19602 return $result;
19603 }
19604 }
19605
19606 $result = $this->loader->load($config, $options);
19607 if ($key) {
19608 $this->cache->save($key, $result);
19609 }
19610
19611 return $result;
19612 }
19613 }
19614 <?php
19615
19616 namespace Guzzle\Service\Resource;
19617
19618 use Guzzle\Common\HasDispatcherInterface;
19619 use Guzzle\Common\ToArrayInterface;
19620
19621 /**
19622 * Iterates over a paginated resource using subsequent requests in order to retrieve the entire matching result set
19623 */
19624 interface ResourceIteratorInterface extends ToArrayInterface, HasDispatcherInterface, \Iterator, \Countable
19625 {
19626 /**
19627 * Retrieve the NextToken that can be used in other iterators.
19628 *
19629 * @return string Returns a NextToken
19630 */
19631 public function getNextToken();
19632
19633 /**
19634 * Attempt to limit the total number of resources returned by the iterator.
19635 *
19636 * You may still receive more items than you specify. Set to 0 to specify no limit.
19637 *
19638 * @param int $limit Limit amount
19639 *
19640 * @return ResourceIteratorInterface
19641 */
19642 public function setLimit($limit);
19643
19644 /**
19645 * Attempt to limit the total number of resources retrieved per request by the iterator.
19646 *
19647 * The iterator may return more than you specify in the page size argument depending on the service and underlying
19648 * command implementation. Set to 0 to specify no page size limitation.
19649 *
19650 * @param int $pageSize Limit amount
19651 *
19652 * @return ResourceIteratorInterface
19653 */
19654 public function setPageSize($pageSize);
19655
19656 /**
19657 * Get a data option from the iterator
19658 *
19659 * @param string $key Key of the option to retrieve
19660 *
19661 * @return mixed|null Returns NULL if not set or the value if set
19662 */
19663 public function get($key);
19664
19665 /**
19666 * Set a data option on the iterator
19667 *
19668 * @param string $key Key of the option to set
19669 * @param mixed $value Value to set for the option
19670 *
19671 * @return ResourceIteratorInterface
19672 */
19673 public function set($key, $value);
19674 }
19675 <?php
19676
19677 namespace Guzzle\Service\Resource;
19678
19679 use Guzzle\Service\Command\CommandInterface;
19680
19681 /**
19682 * Factory for creating {@see ResourceIteratorInterface} objects
19683 */
19684 interface ResourceIteratorFactoryInterface
19685 {
19686 /**
19687 * Create a resource iterator
19688 *
19689 * @param CommandInterface $command Command to create an iterator for
19690 * @param array $options Iterator options that are exposed as data.
19691 *
19692 * @return ResourceIteratorInterface
19693 */
19694 public function build(CommandInterface $command, array $options = array());
19695
19696 /**
19697 * Check if the factory can create an iterator
19698 *
19699 * @param CommandInterface $command Command to create an iterator for
19700 *
19701 * @return bool
19702 */
19703 public function canBuild(CommandInterface $command);
19704 }
19705 <?php
19706
19707 namespace Guzzle\Service\Resource;
19708
19709 use Guzzle\Service\Command\CommandInterface;
19710
19711 /**
19712 * Resource iterator factory used when explicitly mapping strings to iterator classes
19713 */
19714 class MapResourceIteratorFactory extends AbstractResourceIteratorFactory
19715 {
19716 /** @var array Associative array mapping iterator names to class names */
19717 protected $map;
19718
19719 /** @param array $map Associative array mapping iterator names to class names */
19720 public function __construct(array $map)
19721 {
19722 $this->map = $map;
19723 }
19724
19725 public function getClassName(CommandInterface $command)
19726 {
19727 $className = $command->getName();
19728
19729 if (isset($this->map[$className])) {
19730 return $this->map[$className];
19731 } elseif (isset($this->map['*'])) {
19732 // If a wildcard was added, then always use that
19733 return $this->map['*'];
19734 }
19735
19736 return null;
19737 }
19738 }
19739 <?php
19740
19741 namespace Guzzle\Service\Resource;
19742
19743 use Guzzle\Common\AbstractHasDispatcher;
19744 use Guzzle\Service\Command\CommandInterface;
19745
19746 abstract class ResourceIterator extends AbstractHasDispatcher implements ResourceIteratorInterface
19747 {
19748 /** @var CommandInterface Command used to send requests */
19749 protected $command;
19750
19751 /** @var CommandInterface First sent command */
19752 protected $originalCommand;
19753
19754 /** @var array Currently loaded resources */
19755 protected $resources;
19756
19757 /** @var int Total number of resources that have been retrieved */
19758 protected $retrievedCount = 0;
19759
19760 /** @var int Total number of resources that have been iterated */
19761 protected $iteratedCount = 0;
19762
19763 /** @var string NextToken/Marker for a subsequent request */
19764 protected $nextToken = false;
19765
19766 /** @var int Maximum number of resources to fetch per request */
19767 protected $pageSize;
19768
19769 /** @var int Maximum number of resources to retrieve in total */
19770 protected $limit;
19771
19772 /** @var int Number of requests sent */
19773 protected $requestCount = 0;
19774
19775 /** @var array Initial data passed to the constructor */
19776 protected $data = array();
19777
19778 /** @var bool Whether or not the current value is known to be invalid */
19779 protected $invalid;
19780
19781 public static function getAllEvents()
19782 {
19783 return array(
19784 // About to issue another command to get more results
19785 'resource_iterator.before_send',
19786 // Issued another command to get more results
19787 'resource_iterator.after_send'
19788 );
19789 }
19790
19791 /**
19792 * @param CommandInterface $command Initial command used for iteration
19793 * @param array $data Associative array of additional parameters. You may specify any number of custom
19794 * options for an iterator. Among these options, you may also specify the following values:
19795 * - limit: Attempt to limit the maximum number of resources to this amount
19796 * - page_size: Attempt to retrieve this number of resources per request
19797 */
19798 public function __construct(CommandInterface $command, array $data = array())
19799 {
19800 // Clone the command to keep track of the originating command for rewind
19801 $this->originalCommand = $command;
19802
19803 // Parse options from the array of options
19804 $this->data = $data;
19805 $this->limit = array_key_exists('limit', $data) ? $data['limit'] : 0;
19806 $this->pageSize = array_key_exists('page_size', $data) ? $data['page_size'] : false;
19807 }
19808
19809 /**
19810 * Get all of the resources as an array (Warning: this could issue a large number of requests)
19811 *
19812 * @return array
19813 */
19814 public function toArray()
19815 {
19816 return iterator_to_array($this, false);
19817 }
19818
19819 public function setLimit($limit)
19820 {
19821 $this->limit = $limit;
19822 $this->resetState();
19823
19824 return $this;
19825 }
19826
19827 public function setPageSize($pageSize)
19828 {
19829 $this->pageSize = $pageSize;
19830 $this->resetState();
19831
19832 return $this;
19833 }
19834
19835 /**
19836 * Get an option from the iterator
19837 *
19838 * @param string $key Key of the option to retrieve
19839 *
19840 * @return mixed|null Returns NULL if not set or the value if set
19841 */
19842 public function get($key)
19843 {
19844 return array_key_exists($key, $this->data) ? $this->data[$key] : null;
19845 }
19846
19847 /**
19848 * Set an option on the iterator
19849 *
19850 * @param string $key Key of the option to set
19851 * @param mixed $value Value to set for the option
19852 *
19853 * @return ResourceIterator
19854 */
19855 public function set($key, $value)
19856 {
19857 $this->data[$key] = $value;
19858
19859 return $this;
19860 }
19861
19862 public function current()
19863 {
19864 return $this->resources ? current($this->resources) : false;
19865 }
19866
19867 public function key()
19868 {
19869 return max(0, $this->iteratedCount - 1);
19870 }
19871
19872 public function count()
19873 {
19874 return $this->retrievedCount;
19875 }
19876
19877 /**
19878 * Get the total number of requests sent
19879 *
19880 * @return int
19881 */
19882 public function getRequestCount()
19883 {
19884 return $this->requestCount;
19885 }
19886
19887 /**
19888 * Rewind the Iterator to the first element and send the original command
19889 */
19890 public function rewind()
19891 {
19892 // Use the original command
19893 $this->command = clone $this->originalCommand;
19894 $this->resetState();
19895 $this->next();
19896 }
19897
19898 public function valid()
19899 {
19900 return !$this->invalid && (!$this->resources || $this->current() || $this->nextToken)
19901 && (!$this->limit || $this->iteratedCount < $this->limit + 1);
19902 }
19903
19904 public function next()
19905 {
19906 $this->iteratedCount++;
19907
19908 // Check if a new set of resources needs to be retrieved
19909 $sendRequest = false;
19910 if (!$this->resources) {
19911 $sendRequest = true;
19912 } else {
19913 // iterate over the internal array
19914 $current = next($this->resources);
19915 $sendRequest = $current === false && $this->nextToken && (!$this->limit || $this->iteratedCount < $this->limit + 1);
19916 }
19917
19918 if ($sendRequest) {
19919
19920 $this->dispatch('resource_iterator.before_send', array(
19921 'iterator' => $this,
19922 'resources' => $this->resources
19923 ));
19924
19925 // Get a new command object from the original command
19926 $this->command = clone $this->originalCommand;
19927 // Send a request and retrieve the newly loaded resources
19928 $this->resources = $this->sendRequest();
19929 $this->requestCount++;
19930
19931 // If no resources were found, then the last request was not needed
19932 // and iteration must stop
19933 if (empty($this->resources)) {
19934 $this->invalid = true;
19935 } else {
19936 // Add to the number of retrieved resources
19937 $this->retrievedCount += count($this->resources);
19938 // Ensure that we rewind to the beginning of the array
19939 reset($this->resources);
19940 }
19941
19942 $this->dispatch('resource_iterator.after_send', array(
19943 'iterator' => $this,
19944 'resources' => $this->resources
19945 ));
19946 }
19947 }
19948
19949 /**
19950 * Retrieve the NextToken that can be used in other iterators.
19951 *
19952 * @return string Returns a NextToken
19953 */
19954 public function getNextToken()
19955 {
19956 return $this->nextToken;
19957 }
19958
19959 /**
19960 * Returns the value that should be specified for the page size for a request that will maintain any hard limits,
19961 * but still honor the specified pageSize if the number of items retrieved + pageSize < hard limit
19962 *
19963 * @return int Returns the page size of the next request.
19964 */
19965 protected function calculatePageSize()
19966 {
19967 if ($this->limit && $this->iteratedCount + $this->pageSize > $this->limit) {
19968 return 1 + ($this->limit - $this->iteratedCount);
19969 }
19970
19971 return (int) $this->pageSize;
19972 }
19973
19974 /**
19975 * Reset the internal state of the iterator without triggering a rewind()
19976 */
19977 protected function resetState()
19978 {
19979 $this->iteratedCount = 0;
19980 $this->retrievedCount = 0;
19981 $this->nextToken = false;
19982 $this->resources = null;
19983 $this->invalid = false;
19984 }
19985
19986 /**
19987 * Send a request to retrieve the next page of results. Hook for subclasses to implement.
19988 *
19989 * @return array Returns the newly loaded resources
19990 */
19991 abstract protected function sendRequest();
19992 }
19993 <?php
19994
19995 namespace Guzzle\Service\Resource;
19996
19997 use Guzzle\Inflection\InflectorInterface;
19998 use Guzzle\Inflection\Inflector;
19999 use Guzzle\Service\Command\CommandInterface;
20000
20001 /**
20002 * Factory for creating {@see ResourceIteratorInterface} objects using a convention of storing iterator classes under a
20003 * root namespace using the name of a {@see CommandInterface} object as a convention for determining the name of an
20004 * iterator class. The command name is converted to CamelCase and Iterator is appended (e.g. abc_foo => AbcFoo).
20005 */
20006 class ResourceIteratorClassFactory extends AbstractResourceIteratorFactory
20007 {
20008 /** @var array List of namespaces used to look for classes */
20009 protected $namespaces;
20010
20011 /** @var InflectorInterface Inflector used to determine class names */
20012 protected $inflector;
20013
20014 /**
20015 * @param string|array $namespaces List of namespaces for iterator objects
20016 * @param InflectorInterface $inflector Inflector used to resolve class names
20017 */
20018 public function __construct($namespaces = array(), InflectorInterface $inflector = null)
20019 {
20020 $this->namespaces = (array) $namespaces;
20021 $this->inflector = $inflector ?: Inflector::getDefault();
20022 }
20023
20024 /**
20025 * Registers a namespace to check for Iterators
20026 *
20027 * @param string $namespace Namespace which contains Iterator classes
20028 *
20029 * @return self
20030 */
20031 public function registerNamespace($namespace)
20032 {
20033 array_unshift($this->namespaces, $namespace);
20034
20035 return $this;
20036 }
20037
20038 protected function getClassName(CommandInterface $command)
20039 {
20040 $iteratorName = $this->inflector->camel($command->getName()) . 'Iterator';
20041
20042 // Determine the name of the class to load
20043 foreach ($this->namespaces as $namespace) {
20044 $potentialClassName = $namespace . '\\' . $iteratorName;
20045 if (class_exists($potentialClassName)) {
20046 return $potentialClassName;
20047 }
20048 }
20049
20050 return false;
20051 }
20052 }
20053 <?php
20054
20055 namespace Guzzle\Service\Resource;
20056
20057 use Guzzle\Common\Collection;
20058 use Guzzle\Service\Description\Parameter;
20059
20060 /**
20061 * Default model created when commands create service description model responses
20062 */
20063 class Model extends Collection
20064 {
20065 /** @var Parameter Structure of the model */
20066 protected $structure;
20067
20068 /**
20069 * @param array $data Data contained by the model
20070 * @param Parameter $structure The structure of the model
20071 */
20072 public function __construct(array $data = array(), Parameter $structure = null)
20073 {
20074 $this->data = $data;
20075 $this->structure = $structure;
20076 }
20077
20078 /**
20079 * Get the structure of the model
20080 *
20081 * @return Parameter
20082 */
20083 public function getStructure()
20084 {
20085 return $this->structure ?: new Parameter();
20086 }
20087
20088 /**
20089 * Provides debug information about the model object
20090 *
20091 * @return string
20092 */
20093 public function __toString()
20094 {
20095 $output = 'Debug output of ';
20096 if ($this->structure) {
20097 $output .= $this->structure->getName() . ' ';
20098 }
20099 $output .= 'model';
20100 $output = str_repeat('=', strlen($output)) . "\n" . $output . "\n" . str_repeat('=', strlen($output)) . "\n\n";
20101 $output .= "Model data\n-----------\n\n";
20102 $output .= "This data can be retrieved from the model object using the get() method of the model "
20103 . "(e.g. \$model->get(\$key)) or accessing the model like an associative array (e.g. \$model['key']).\n\n";
20104 $lines = array_slice(explode("\n", trim(print_r($this->toArray(), true))), 2, -1);
20105 $output .= implode("\n", $lines);
20106
20107 if ($this->structure) {
20108 $output .= "\n\nModel structure\n---------------\n\n";
20109 $output .= "The following JSON document defines how the model was parsed from an HTTP response into the "
20110 . "associative array strucure you see above.\n\n";
20111 $output .= ' ' . json_encode($this->structure->toArray()) . "\n\n";
20112 }
20113
20114 return $output . "\n";
20115 }
20116 }
20117 <?php
20118
20119 namespace Guzzle\Service\Resource;
20120
20121 use Guzzle\Common\Exception\InvalidArgumentException;
20122 use Guzzle\Service\Command\CommandInterface;
20123
20124 /**
20125 * Factory that utilizes multiple factories for creating iterators
20126 */
20127 class CompositeResourceIteratorFactory implements ResourceIteratorFactoryInterface
20128 {
20129 /** @var array Array of factories */
20130 protected $factories;
20131
20132 /** @param array $factories Array of factories used to instantiate iterators */
20133 public function __construct(array $factories)
20134 {
20135 $this->factories = $factories;
20136 }
20137
20138 public function build(CommandInterface $command, array $options = array())
20139 {
20140 if (!($factory = $this->getFactory($command))) {
20141 throw new InvalidArgumentException('Iterator was not found for ' . $command->getName());
20142 }
20143
20144 return $factory->build($command, $options);
20145 }
20146
20147 public function canBuild(CommandInterface $command)
20148 {
20149 return $this->getFactory($command) !== false;
20150 }
20151
20152 /**
20153 * Add a factory to the composite factory
20154 *
20155 * @param ResourceIteratorFactoryInterface $factory Factory to add
20156 *
20157 * @return self
20158 */
20159 public function addFactory(ResourceIteratorFactoryInterface $factory)
20160 {
20161 $this->factories[] = $factory;
20162
20163 return $this;
20164 }
20165
20166 /**
20167 * Get the factory that matches the command object
20168 *
20169 * @param CommandInterface $command Command retrieving the iterator for
20170 *
20171 * @return ResourceIteratorFactoryInterface|bool
20172 */
20173 protected function getFactory(CommandInterface $command)
20174 {
20175 foreach ($this->factories as $factory) {
20176 if ($factory->canBuild($command)) {
20177 return $factory;
20178 }
20179 }
20180
20181 return false;
20182 }
20183 }
20184 <?php
20185
20186 namespace Guzzle\Service\Resource;
20187
20188 use Guzzle\Common\AbstractHasDispatcher;
20189 use Guzzle\Batch\BatchBuilder;
20190 use Guzzle\Batch\BatchSizeDivisor;
20191 use Guzzle\Batch\BatchClosureTransfer;
20192 use Guzzle\Common\Version;
20193
20194 /**
20195 * Apply a callback to the contents of a {@see ResourceIteratorInterface}
20196 * @deprecated Will be removed in a future version and is no longer maintained. Use the Batch\ abstractions instead.
20197 * @codeCoverageIgnore
20198 */
20199 class ResourceIteratorApplyBatched extends AbstractHasDispatcher
20200 {
20201 /** @var callable|array */
20202 protected $callback;
20203
20204 /** @var ResourceIteratorInterface */
20205 protected $iterator;
20206
20207 /** @var integer Total number of sent batches */
20208 protected $batches = 0;
20209
20210 /** @var int Total number of iterated resources */
20211 protected $iterated = 0;
20212
20213 public static function getAllEvents()
20214 {
20215 return array(
20216 // About to send a batch of requests to the callback
20217 'iterator_batch.before_batch',
20218 // Finished sending a batch of requests to the callback
20219 'iterator_batch.after_batch',
20220 // Created the batch object
20221 'iterator_batch.created_batch'
20222 );
20223 }
20224
20225 /**
20226 * @param ResourceIteratorInterface $iterator Resource iterator to apply a callback to
20227 * @param array|callable $callback Callback method accepting the resource iterator
20228 * and an array of the iterator's current resources
20229 */
20230 public function __construct(ResourceIteratorInterface $iterator, $callback)
20231 {
20232 $this->iterator = $iterator;
20233 $this->callback = $callback;
20234 Version::warn(__CLASS__ . ' is deprecated');
20235 }
20236
20237 /**
20238 * Apply the callback to the contents of the resource iterator
20239 *
20240 * @param int $perBatch The number of records to group per batch transfer
20241 *
20242 * @return int Returns the number of iterated resources
20243 */
20244 public function apply($perBatch = 50)
20245 {
20246 $this->iterated = $this->batches = $batches = 0;
20247 $that = $this;
20248 $it = $this->iterator;
20249 $callback = $this->callback;
20250
20251 $batch = BatchBuilder::factory()
20252 ->createBatchesWith(new BatchSizeDivisor($perBatch))
20253 ->transferWith(new BatchClosureTransfer(function (array $batch) use ($that, $callback, &$batches, $it) {
20254 $batches++;
20255 $that->dispatch('iterator_batch.before_batch', array('iterator' => $it, 'batch' => $batch));
20256 call_user_func_array($callback, array($it, $batch));
20257 $that->dispatch('iterator_batch.after_batch', array('iterator' => $it, 'batch' => $batch));
20258 }))
20259 ->autoFlushAt($perBatch)
20260 ->build();
20261
20262 $this->dispatch('iterator_batch.created_batch', array('batch' => $batch));
20263
20264 foreach ($this->iterator as $resource) {
20265 $this->iterated++;
20266 $batch->add($resource);
20267 }
20268
20269 $batch->flush();
20270 $this->batches = $batches;
20271
20272 return $this->iterated;
20273 }
20274
20275 /**
20276 * Get the total number of batches sent
20277 *
20278 * @return int
20279 */
20280 public function getBatchCount()
20281 {
20282 return $this->batches;
20283 }
20284
20285 /**
20286 * Get the total number of iterated resources
20287 *
20288 * @return int
20289 */
20290 public function getIteratedCount()
20291 {
20292 return $this->iterated;
20293 }
20294 }
20295 <?php
20296
20297 namespace Guzzle\Service\Resource;
20298
20299 use Guzzle\Common\Exception\InvalidArgumentException;
20300 use Guzzle\Service\Command\CommandInterface;
20301
20302 /**
20303 * Abstract resource iterator factory implementation
20304 */
20305 abstract class AbstractResourceIteratorFactory implements ResourceIteratorFactoryInterface
20306 {
20307 public function build(CommandInterface $command, array $options = array())
20308 {
20309 if (!$this->canBuild($command)) {
20310 throw new InvalidArgumentException('Iterator was not found for ' . $command->getName());
20311 }
20312
20313 $className = $this->getClassName($command);
20314
20315 return new $className($command, $options);
20316 }
20317
20318 public function canBuild(CommandInterface $command)
20319 {
20320 return (bool) $this->getClassName($command);
20321 }
20322
20323 /**
20324 * Get the name of the class to instantiate for the command
20325 *
20326 * @param CommandInterface $command Command that is associated with the iterator
20327 *
20328 * @return string
20329 */
20330 abstract protected function getClassName(CommandInterface $command);
20331 }
20332 <?php
20333
20334 namespace Guzzle\Service\Exception;
20335
20336 use Guzzle\Common\Exception\RuntimeException;
20337
20338 class ValidationException extends RuntimeException
20339 {
20340 protected $errors = array();
20341
20342 /**
20343 * Set the validation error messages
20344 *
20345 * @param array $errors Array of validation errors
20346 */
20347 public function setErrors(array $errors)
20348 {
20349 $this->errors = $errors;
20350 }
20351
20352 /**
20353 * Get any validation errors
20354 *
20355 * @return array
20356 */
20357 public function getErrors()
20358 {
20359 return $this->errors;
20360 }
20361 }
20362 <?php
20363
20364 namespace Guzzle\Service\Exception;
20365
20366 use Guzzle\Http\Exception\MultiTransferException;
20367 use Guzzle\Service\Command\CommandInterface;
20368
20369 /**
20370 * Exception thrown when transferring commands in parallel
20371 */
20372 class CommandTransferException extends MultiTransferException
20373 {
20374 protected $successfulCommands = array();
20375 protected $failedCommands = array();
20376
20377 /**
20378 * Creates a new CommandTransferException from a MultiTransferException
20379 *
20380 * @param MultiTransferException $e Exception to base a new exception on
20381 *
20382 * @return self
20383 */
20384 public static function fromMultiTransferException(MultiTransferException $e)
20385 {
20386 $ce = new self($e->getMessage(), $e->getCode(), $e->getPrevious());
20387 $ce->setSuccessfulRequests($e->getSuccessfulRequests());
20388
20389 $alreadyAddedExceptions = array();
20390 foreach ($e->getFailedRequests() as $request) {
20391 if ($re = $e->getExceptionForFailedRequest($request)) {
20392 $alreadyAddedExceptions[] = $re;
20393 $ce->addFailedRequestWithException($request, $re);
20394 } else {
20395 $ce->addFailedRequest($request);
20396 }
20397 }
20398
20399 // Add any exceptions that did not map to a request
20400 if (count($alreadyAddedExceptions) < count($e)) {
20401 foreach ($e as $ex) {
20402 if (!in_array($ex, $alreadyAddedExceptions)) {
20403 $ce->add($ex);
20404 }
20405 }
20406 }
20407
20408 return $ce;
20409 }
20410
20411 /**
20412 * Get all of the commands in the transfer
20413 *
20414 * @return array
20415 */
20416 public function getAllCommands()
20417 {
20418 return array_merge($this->successfulCommands, $this->failedCommands);
20419 }
20420
20421 /**
20422 * Add to the array of successful commands
20423 *
20424 * @param CommandInterface $command Successful command
20425 *
20426 * @return self
20427 */
20428 public function addSuccessfulCommand(CommandInterface $command)
20429 {
20430 $this->successfulCommands[] = $command;
20431
20432 return $this;
20433 }
20434
20435 /**
20436 * Add to the array of failed commands
20437 *
20438 * @param CommandInterface $command Failed command
20439 *
20440 * @return self
20441 */
20442 public function addFailedCommand(CommandInterface $command)
20443 {
20444 $this->failedCommands[] = $command;
20445
20446 return $this;
20447 }
20448
20449 /**
20450 * Get an array of successful commands
20451 *
20452 * @return array
20453 */
20454 public function getSuccessfulCommands()
20455 {
20456 return $this->successfulCommands;
20457 }
20458
20459 /**
20460 * Get an array of failed commands
20461 *
20462 * @return array
20463 */
20464 public function getFailedCommands()
20465 {
20466 return $this->failedCommands;
20467 }
20468
20469 /**
20470 * Get the Exception that caused the given $command to fail
20471 *
20472 * @param CommandInterface $command Failed command
20473 *
20474 * @return \Exception|null
20475 */
20476 public function getExceptionForFailedCommand(CommandInterface $command)
20477 {
20478 return $this->getExceptionForFailedRequest($command->getRequest());
20479 }
20480 }
20481 <?php
20482
20483 namespace Guzzle\Service\Exception;
20484
20485 use Guzzle\Common\Exception\RuntimeException;
20486
20487 class DescriptionBuilderException extends RuntimeException {}
20488 <?php
20489
20490 namespace Guzzle\Service\Exception;
20491
20492 use Guzzle\Common\Exception\RuntimeException;
20493
20494 class ServiceBuilderException extends RuntimeException {}
20495 <?php
20496
20497 namespace Guzzle\Service\Exception;
20498
20499 use Guzzle\Common\Exception\RuntimeException;
20500
20501 class ResponseClassException extends RuntimeException
20502 {
20503 }
20504 <?php
20505
20506 namespace Guzzle\Service\Exception;
20507
20508 class ServiceNotFoundException extends ServiceBuilderException {}
20509 <?php
20510
20511 namespace Guzzle\Service\Exception;
20512
20513 use Guzzle\Common\Exception\RuntimeException;
20514
20515 class CommandException extends RuntimeException {}
20516 <?php
20517
20518 namespace Guzzle\Service\Exception;
20519
20520 use Guzzle\Common\Exception\RuntimeException;
20521
20522 /**
20523 * Command transfer exception when commands do not all use the same client
20524 */
20525 class InconsistentClientTransferException extends RuntimeException
20526 {
20527 /**
20528 * @var array Commands with an invalid client
20529 */
20530 private $invalidCommands = array();
20531
20532 /**
20533 * @param array $commands Invalid commands
20534 */
20535 public function __construct(array $commands)
20536 {
20537 $this->invalidCommands = $commands;
20538 parent::__construct(
20539 'Encountered commands in a batch transfer that use inconsistent clients. The batching ' .
20540 'strategy you use with a command transfer must divide command batches by client.'
20541 );
20542 }
20543
20544 /**
20545 * Get the invalid commands
20546 *
20547 * @return array
20548 */
20549 public function getCommands()
20550 {
20551 return $this->invalidCommands;
20552 }
20553 }
20554 {
20555 "name": "guzzle/service",
20556 "description": "Guzzle service component for abstracting RESTful web services",
20557 "homepage": "http://guzzlephp.org/",
20558 "keywords": ["web service", "webservice", "REST", "guzzle"],
20559 "license": "MIT",
20560 "authors": [
20561 {
20562 "name": "Michael Dowling",
20563 "email": "mtdowling@gmail.com",
20564 "homepage": "https://github.com/mtdowling"
20565 }
20566 ],
20567 "require": {
20568 "php": ">=5.3.2",
20569 "guzzle/cache": "self.version",
20570 "guzzle/http": "self.version",
20571 "guzzle/inflection": "self.version"
20572 },
20573 "autoload": {
20574 "psr-0": { "Guzzle\\Service": "" }
20575 },
20576 "target-dir": "Guzzle/Service",
20577 "extra": {
20578 "branch-alias": {
20579 "dev-master": "3.7-dev"
20580 }
20581 }
20582 }
20583 <?php
20584
20585 namespace Guzzle\Service\Builder;
20586
20587 use Guzzle\Common\AbstractHasDispatcher;
20588 use Guzzle\Service\ClientInterface;
20589 use Guzzle\Service\Exception\ServiceBuilderException;
20590 use Guzzle\Service\Exception\ServiceNotFoundException;
20591 use Symfony\Component\EventDispatcher\EventSubscriberInterface;
20592
20593 /**
20594 * {@inheritdoc}
20595 *
20596 * Clients and data can be set, retrieved, and removed by accessing the service builder like an associative array.
20597 */
20598 class ServiceBuilder extends AbstractHasDispatcher implements ServiceBuilderInterface, \ArrayAccess, \Serializable
20599 {
20600 /** @var array Service builder configuration data */
20601 protected $builderConfig = array();
20602
20603 /** @var array Instantiated client objects */
20604 protected $clients = array();
20605
20606 /** @var ServiceBuilderLoader Cached instance of the service builder loader */
20607 protected static $cachedFactory;
20608
20609 /** @var array Plugins to attach to each client created by the service builder */
20610 protected $plugins = array();
20611
20612 /**
20613 * Create a new ServiceBuilder using configuration data sourced from an
20614 * array, .js|.json or .php file.
20615 *
20616 * @param array|string $config The full path to an .json|.js or .php file, or an associative array
20617 * @param array $globalParameters Array of global parameters to pass to every service as it is instantiated.
20618 *
20619 * @return ServiceBuilderInterface
20620 * @throws ServiceBuilderException if a file cannot be opened
20621 * @throws ServiceNotFoundException when trying to extend a missing client
20622 */
20623 public static function factory($config = null, array $globalParameters = array())
20624 {
20625 // @codeCoverageIgnoreStart
20626 if (!static::$cachedFactory) {
20627 static::$cachedFactory = new ServiceBuilderLoader();
20628 }
20629 // @codeCoverageIgnoreEnd
20630
20631 return self::$cachedFactory->load($config, $globalParameters);
20632 }
20633
20634 /**
20635 * @param array $serviceBuilderConfig Service configuration settings:
20636 * - name: Name of the service
20637 * - class: Client class to instantiate using a factory method
20638 * - params: array of key value pair configuration settings for the builder
20639 */
20640 public function __construct(array $serviceBuilderConfig = array())
20641 {
20642 $this->builderConfig = $serviceBuilderConfig;
20643 }
20644
20645 public static function getAllEvents()
20646 {
20647 return array('service_builder.create_client');
20648 }
20649
20650 public function unserialize($serialized)
20651 {
20652 $this->builderConfig = json_decode($serialized, true);
20653 }
20654
20655 public function serialize()
20656 {
20657 return json_encode($this->builderConfig);
20658 }
20659
20660 /**
20661 * Attach a plugin to every client created by the builder
20662 *
20663 * @param EventSubscriberInterface $plugin Plugin to attach to each client
20664 *
20665 * @return self
20666 */
20667 public function addGlobalPlugin(EventSubscriberInterface $plugin)
20668 {
20669 $this->plugins[] = $plugin;
20670
20671 return $this;
20672 }
20673
20674 /**
20675 * Get data from the service builder without triggering the building of a service
20676 *
20677 * @param string $name Name of the service to retrieve
20678 *
20679 * @return array|null
20680 */
20681 public function getData($name)
20682 {
20683 return isset($this->builderConfig[$name]) ? $this->builderConfig[$name] : null;
20684 }
20685
20686 public function get($name, $throwAway = false)
20687 {
20688 if (!isset($this->builderConfig[$name])) {
20689
20690 // Check to see if arbitrary data is being referenced
20691 if (isset($this->clients[$name])) {
20692 return $this->clients[$name];
20693 }
20694
20695 // Check aliases and return a match if found
20696 foreach ($this->builderConfig as $actualName => $config) {
20697 if (isset($config['alias']) && $config['alias'] == $name) {
20698 return $this->get($actualName, $throwAway);
20699 }
20700 }
20701 throw new ServiceNotFoundException('No service is registered as ' . $name);
20702 }
20703
20704 if (!$throwAway && isset($this->clients[$name])) {
20705 return $this->clients[$name];
20706 }
20707
20708 $builder =& $this->builderConfig[$name];
20709
20710 // Convert references to the actual client
20711 foreach ($builder['params'] as &$v) {
20712 if (is_string($v) && substr($v, 0, 1) == '{' && substr($v, -1) == '}') {
20713 $v = $this->get(trim($v, '{} '));
20714 }
20715 }
20716
20717 // Get the configured parameters and merge in any parameters provided for throw-away clients
20718 $config = $builder['params'];
20719 if (is_array($throwAway)) {
20720 $config = $throwAway + $config;
20721 }
20722
20723 $client = $builder['class']::factory($config);
20724
20725 if (!$throwAway) {
20726 $this->clients[$name] = $client;
20727 }
20728
20729 if ($client instanceof ClientInterface) {
20730 foreach ($this->plugins as $plugin) {
20731 $client->addSubscriber($plugin);
20732 }
20733 // Dispatch an event letting listeners know a client was created
20734 $this->dispatch('service_builder.create_client', array('client' => $client));
20735 }
20736
20737 return $client;
20738 }
20739
20740 public function set($key, $service)
20741 {
20742 if (is_array($service) && isset($service['class']) && isset($service['params'])) {
20743 $this->builderConfig[$key] = $service;
20744 } else {
20745 $this->clients[$key] = $service;
20746 }
20747
20748 return $this;
20749 }
20750
20751 public function offsetSet($offset, $value)
20752 {
20753 $this->set($offset, $value);
20754 }
20755
20756 public function offsetUnset($offset)
20757 {
20758 unset($this->builderConfig[$offset]);
20759 unset($this->clients[$offset]);
20760 }
20761
20762 public function offsetExists($offset)
20763 {
20764 return isset($this->builderConfig[$offset]) || isset($this->clients[$offset]);
20765 }
20766
20767 public function offsetGet($offset)
20768 {
20769 return $this->get($offset);
20770 }
20771 }
20772 <?php
20773
20774 namespace Guzzle\Service\Builder;
20775
20776 use Guzzle\Service\AbstractConfigLoader;
20777 use Guzzle\Service\Exception\ServiceNotFoundException;
20778
20779 /**
20780 * Service builder config loader
20781 */
20782 class ServiceBuilderLoader extends AbstractConfigLoader
20783 {
20784 protected function build($config, array $options)
20785 {
20786 // A service builder class can be specified in the class field
20787 $class = !empty($config['class']) ? $config['class'] : __NAMESPACE__ . '\\ServiceBuilder';
20788
20789 // Account for old style configs that do not have a services array
20790 $services = isset($config['services']) ? $config['services'] : $config;
20791
20792 // Validate the configuration and handle extensions
20793 foreach ($services as $name => &$service) {
20794
20795 $service['params'] = isset($service['params']) ? $service['params'] : array();
20796
20797 // Check if this client builder extends another client
20798 if (!empty($service['extends'])) {
20799
20800 // Make sure that the service it's extending has been defined
20801 if (!isset($services[$service['extends']])) {
20802 throw new ServiceNotFoundException(
20803 "{$name} is trying to extend a non-existent service: {$service['extends']}"
20804 );
20805 }
20806
20807 $extended = &$services[$service['extends']];
20808
20809 // Use the correct class attribute
20810 if (empty($service['class'])) {
20811 $service['class'] = isset($extended['class']) ? $extended['class'] : '';
20812 }
20813 if ($extendsParams = isset($extended['params']) ? $extended['params'] : false) {
20814 $service['params'] = $service['params'] + $extendsParams;
20815 }
20816 }
20817
20818 // Overwrite default values with global parameter values
20819 if (!empty($options)) {
20820 $service['params'] = $options + $service['params'];
20821 }
20822
20823 $service['class'] = isset($service['class']) ? $service['class'] : '';
20824 }
20825
20826 return new $class($services);
20827 }
20828
20829 protected function mergeData(array $a, array $b)
20830 {
20831 $result = $b + $a;
20832
20833 // Merge services using a recursive union of arrays
20834 if (isset($a['services']) && $b['services']) {
20835
20836 // Get a union of the services of the two arrays
20837 $result['services'] = $b['services'] + $a['services'];
20838
20839 // Merge each service in using a union of the two arrays
20840 foreach ($result['services'] as $name => &$service) {
20841
20842 // By default, services completely override a previously defined service unless it extends itself
20843 if (isset($a['services'][$name]['extends'])
20844 && isset($b['services'][$name]['extends'])
20845 && $b['services'][$name]['extends'] == $name
20846 ) {
20847 $service += $a['services'][$name];
20848 // Use the `extends` attribute of the parent
20849 $service['extends'] = $a['services'][$name]['extends'];
20850 // Merge parameters using a union if both have parameters
20851 if (isset($a['services'][$name]['params'])) {
20852 $service['params'] += $a['services'][$name]['params'];
20853 }
20854 }
20855 }
20856 }
20857
20858 return $result;
20859 }
20860 }
20861 <?php
20862
20863 namespace Guzzle\Service\Builder;
20864
20865 use Guzzle\Service\Exception\ServiceNotFoundException;
20866
20867 /**
20868 * Service builder used to store and build clients or arbitrary data. Client configuration data can be supplied to tell
20869 * the service builder how to create and cache {@see \Guzzle\Service\ClientInterface} objects. Arbitrary data can be
20870 * supplied and accessed from a service builder. Arbitrary data and other clients can be referenced by name in client
20871 * configuration arrays to make them input for building other clients (e.g. "{key}").
20872 */
20873 interface ServiceBuilderInterface
20874 {
20875 /**
20876 * Get a ClientInterface object or arbitrary data from the service builder
20877 *
20878 * @param string $name Name of the registered service or data to retrieve
20879 * @param bool|array $throwAway Only pertains to retrieving client objects built using a configuration array.
20880 * Set to TRUE to not store the client for later retrieval from the ServiceBuilder.
20881 * If an array is specified, that data will overwrite the configured params of the
20882 * client if the client implements {@see \Guzzle\Common\FromConfigInterface} and will
20883 * not store the client for later retrieval.
20884 *
20885 * @return \Guzzle\Service\ClientInterface|mixed
20886 * @throws ServiceNotFoundException when a client or data cannot be found by the given name
20887 */
20888 public function get($name, $throwAway = false);
20889
20890 /**
20891 * Register a service or arbitrary data by name with the service builder
20892 *
20893 * @param string $key Name of the client or data to register
20894 * @param mixed $service Client configuration array or arbitrary data to register. The client configuration array
20895 * must include a 'class' (string) and 'params' (array) key.
20896 *
20897 * @return ServiceBuilderInterface
20898 */
20899 public function set($key, $service);
20900 }
20901 <?php
20902
20903 namespace Guzzle\Service\Command;
20904
20905 use Guzzle\Common\Exception\InvalidArgumentException;
20906 use Guzzle\Common\Exception\UnexpectedValueException;
20907 use Guzzle\Http\Message\RequestInterface;
20908
20909 /**
20910 * A ClosureCommand is a command that allows dynamic commands to be created at runtime using a closure to prepare the
20911 * request. A closure key and \Closure value must be passed to the command in the constructor. The closure must
20912 * accept the command object as an argument.
20913 */
20914 class ClosureCommand extends AbstractCommand
20915 {
20916 /**
20917 * {@inheritdoc}
20918 * @throws InvalidArgumentException if a closure was not passed
20919 */
20920 protected function init()
20921 {
20922 if (!$this['closure']) {
20923 throw new InvalidArgumentException('A closure must be passed in the parameters array');
20924 }
20925 }
20926
20927 /**
20928 * {@inheritdoc}
20929 * @throws UnexpectedValueException If the closure does not return a request
20930 */
20931 protected function build()
20932 {
20933 $closure = $this['closure'];
20934 /** @var $closure \Closure */
20935 $this->request = $closure($this, $this->operation);
20936
20937 if (!$this->request || !$this->request instanceof RequestInterface) {
20938 throw new UnexpectedValueException('Closure command did not return a RequestInterface object');
20939 }
20940 }
20941 }
20942 <?php
20943
20944 namespace Guzzle\Service\Command;
20945
20946 use Guzzle\Http\Message\RequestInterface;
20947 use Guzzle\Http\Url;
20948 use Guzzle\Service\Command\LocationVisitor\Request\RequestVisitorInterface;
20949 use Guzzle\Service\Command\LocationVisitor\VisitorFlyweight;
20950 use Guzzle\Service\Description\OperationInterface;
20951 use Guzzle\Service\Description\Parameter;
20952
20953 /**
20954 * Default request serializer that transforms command options and operation parameters into a request
20955 */
20956 class DefaultRequestSerializer implements RequestSerializerInterface
20957 {
20958 /** @var VisitorFlyweight $factory Visitor factory */
20959 protected $factory;
20960
20961 /** @var self */
20962 protected static $instance;
20963
20964 /**
20965 * @return self
20966 * @codeCoverageIgnore
20967 */
20968 public static function getInstance()
20969 {
20970 if (!self::$instance) {
20971 self::$instance = new self(VisitorFlyweight::getInstance());
20972 }
20973
20974 return self::$instance;
20975 }
20976
20977 /**
20978 * @param VisitorFlyweight $factory Factory to use when creating visitors
20979 */
20980 public function __construct(VisitorFlyweight $factory)
20981 {
20982 $this->factory = $factory;
20983 }
20984
20985 /**
20986 * Add a location visitor to the serializer
20987 *
20988 * @param string $location Location to associate with the visitor
20989 * @param RequestVisitorInterface $visitor Visitor to attach
20990 *
20991 * @return self
20992 */
20993 public function addVisitor($location, RequestVisitorInterface $visitor)
20994 {
20995 $this->factory->addRequestVisitor($location, $visitor);
20996
20997 return $this;
20998 }
20999
21000 public function prepare(CommandInterface $command)
21001 {
21002 $request = $this->createRequest($command);
21003 // Keep an array of visitors found in the operation
21004 $foundVisitors = array();
21005 $operation = $command->getOperation();
21006
21007 // Add arguments to the request using the location attribute
21008 foreach ($operation->getParams() as $name => $arg) {
21009 /** @var $arg \Guzzle\Service\Description\Parameter */
21010 $location = $arg->getLocation();
21011 // Skip 'uri' locations because they've already been processed
21012 if ($location && $location != 'uri') {
21013 // Instantiate visitors as they are detected in the properties
21014 if (!isset($foundVisitors[$location])) {
21015 $foundVisitors[$location] = $this->factory->getRequestVisitor($location);
21016 }
21017 // Ensure that a value has been set for this parameter
21018 $value = $command[$name];
21019 if ($value !== null) {
21020 // Apply the parameter value with the location visitor
21021 $foundVisitors[$location]->visit($command, $request, $arg, $value);
21022 }
21023 }
21024 }
21025
21026 // Serialize additional parameters
21027 if ($additional = $operation->getAdditionalParameters()) {
21028 if ($visitor = $this->prepareAdditionalParameters($operation, $command, $request, $additional)) {
21029 $foundVisitors[$additional->getLocation()] = $visitor;
21030 }
21031 }
21032
21033 // Call the after method on each visitor found in the operation
21034 foreach ($foundVisitors as $visitor) {
21035 $visitor->after($command, $request);
21036 }
21037
21038 return $request;
21039 }
21040
21041 /**
21042 * Serialize additional parameters
21043 *
21044 * @param OperationInterface $operation Operation that owns the command
21045 * @param CommandInterface $command Command to prepare
21046 * @param RequestInterface $request Request to serialize
21047 * @param Parameter $additional Additional parameters
21048 *
21049 * @return null|RequestVisitorInterface
21050 */
21051 protected function prepareAdditionalParameters(
21052 OperationInterface $operation,
21053 CommandInterface $command,
21054 RequestInterface $request,
21055 Parameter $additional
21056 ) {
21057 if (!($location = $additional->getLocation())) {
21058 return;
21059 }
21060
21061 $visitor = $this->factory->getRequestVisitor($location);
21062 $hidden = $command[$command::HIDDEN_PARAMS];
21063
21064 foreach ($command->toArray() as $key => $value) {
21065 // Ignore values that are null or built-in command options
21066 if ($value !== null
21067 && !in_array($key, $hidden)
21068 && !$operation->hasParam($key)
21069 ) {
21070 $additional->setName($key);
21071 $visitor->visit($command, $request, $additional, $value);
21072 }
21073 }
21074
21075 return $visitor;
21076 }
21077
21078 /**
21079 * Create a request for the command and operation
21080 *
21081 * @param CommandInterface $command Command to create a request for
21082 *
21083 * @return RequestInterface
21084 */
21085 protected function createRequest(CommandInterface $command)
21086 {
21087 $operation = $command->getOperation();
21088 $client = $command->getClient();
21089 $options = $command[AbstractCommand::REQUEST_OPTIONS] ?: array();
21090
21091 // If the command does not specify a template, then assume the base URL of the client
21092 if (!($uri = $operation->getUri())) {
21093 return $client->createRequest($operation->getHttpMethod(), $client->getBaseUrl(), null, null, $options);
21094 }
21095
21096 // Get the path values and use the client config settings
21097 $variables = array();
21098 foreach ($operation->getParams() as $name => $arg) {
21099 if ($arg->getLocation() == 'uri') {
21100 if (isset($command[$name])) {
21101 $variables[$name] = $arg->filter($command[$name]);
21102 if (!is_array($variables[$name])) {
21103 $variables[$name] = (string) $variables[$name];
21104 }
21105 }
21106 }
21107 }
21108
21109 return $client->createRequest($operation->getHttpMethod(), array($uri, $variables), null, null, $options);
21110 }
21111 }
21112 <?php
21113
21114 namespace Guzzle\Service\Command;
21115
21116 /**
21117 * Interface used to accept a completed OperationCommand and parse the result into a specific response type
21118 */
21119 interface ResponseClassInterface
21120 {
21121 /**
21122 * Create a response model object from a completed command
21123 *
21124 * @param OperationCommand $command That serialized the request
21125 *
21126 * @return self
21127 */
21128 public static function fromCommand(OperationCommand $command);
21129 }
21130 <?php
21131
21132 namespace Guzzle\Service\Command;
21133
21134 /**
21135 * Parses the HTTP response of a command and sets the appropriate result on a command object
21136 */
21137 interface ResponseParserInterface
21138 {
21139 /**
21140 * Parse the HTTP response received by the command and update the command's result contents
21141 *
21142 * @param CommandInterface $command Command to parse and update
21143 *
21144 * @return mixed Returns the result to set on the command
21145 */
21146 public function parse(CommandInterface $command);
21147 }
21148 <?php
21149
21150 namespace Guzzle\Service\Command\Factory;
21151
21152 /**
21153 * Command factory used when explicitly mapping strings to command classes
21154 */
21155 class MapFactory implements FactoryInterface
21156 {
21157 /** @var array Associative array mapping command names to classes */
21158 protected $map;
21159
21160 /** @param array $map Associative array mapping command names to classes */
21161 public function __construct(array $map)
21162 {
21163 $this->map = $map;
21164 }
21165
21166 public function factory($name, array $args = array())
21167 {
21168 if (isset($this->map[$name])) {
21169 $class = $this->map[$name];
21170
21171 return new $class($args);
21172 }
21173 }
21174 }
21175 <?php
21176
21177 namespace Guzzle\Service\Command\Factory;
21178
21179 use Guzzle\Service\Command\CommandInterface;
21180 use Guzzle\Service\ClientInterface;
21181
21182 /**
21183 * Composite factory used by a client object to create command objects utilizing multiple factories
21184 */
21185 class CompositeFactory implements \IteratorAggregate, \Countable, FactoryInterface
21186 {
21187 /** @var array Array of command factories */
21188 protected $factories;
21189
21190 /**
21191 * Get the default chain to use with clients
21192 *
21193 * @param ClientInterface $client Client to base the chain on
21194 *
21195 * @return self
21196 */
21197 public static function getDefaultChain(ClientInterface $client)
21198 {
21199 $factories = array();
21200 if ($description = $client->getDescription()) {
21201 $factories[] = new ServiceDescriptionFactory($description);
21202 }
21203 $factories[] = new ConcreteClassFactory($client);
21204
21205 return new self($factories);
21206 }
21207
21208 /**
21209 * @param array $factories Array of command factories
21210 */
21211 public function __construct(array $factories = array())
21212 {
21213 $this->factories = $factories;
21214 }
21215
21216 /**
21217 * Add a command factory to the chain
21218 *
21219 * @param FactoryInterface $factory Factory to add
21220 * @param string|FactoryInterface $before Insert the new command factory before a command factory class or object
21221 * matching a class name.
21222 * @return CompositeFactory
21223 */
21224 public function add(FactoryInterface $factory, $before = null)
21225 {
21226 $pos = null;
21227
21228 if ($before) {
21229 foreach ($this->factories as $i => $f) {
21230 if ($before instanceof FactoryInterface) {
21231 if ($f === $before) {
21232 $pos = $i;
21233 break;
21234 }
21235 } elseif (is_string($before)) {
21236 if ($f instanceof $before) {
21237 $pos = $i;
21238 break;
21239 }
21240 }
21241 }
21242 }
21243
21244 if ($pos === null) {
21245 $this->factories[] = $factory;
21246 } else {
21247 array_splice($this->factories, $i, 0, array($factory));
21248 }
21249
21250 return $this;
21251 }
21252
21253 /**
21254 * Check if the chain contains a specific command factory
21255 *
21256 * @param FactoryInterface|string $factory Factory to check
21257 *
21258 * @return bool
21259 */
21260 public function has($factory)
21261 {
21262 return (bool) $this->find($factory);
21263 }
21264
21265 /**
21266 * Remove a specific command factory from the chain
21267 *
21268 * @param string|FactoryInterface $factory Factory to remove by name or instance
21269 *
21270 * @return CompositeFactory
21271 */
21272 public function remove($factory = null)
21273 {
21274 if (!($factory instanceof FactoryInterface)) {
21275 $factory = $this->find($factory);
21276 }
21277
21278 $this->factories = array_values(array_filter($this->factories, function($f) use ($factory) {
21279 return $f !== $factory;
21280 }));
21281
21282 return $this;
21283 }
21284
21285 /**
21286 * Get a command factory by class name
21287 *
21288 * @param string|FactoryInterface $factory Command factory class or instance
21289 *
21290 * @return null|FactoryInterface
21291 */
21292 public function find($factory)
21293 {
21294 foreach ($this->factories as $f) {
21295 if ($factory === $f || (is_string($factory) && $f instanceof $factory)) {
21296 return $f;
21297 }
21298 }
21299 }
21300
21301 /**
21302 * Create a command using the associated command factories
21303 *
21304 * @param string $name Name of the command
21305 * @param array $args Command arguments
21306 *
21307 * @return CommandInterface
21308 */
21309 public function factory($name, array $args = array())
21310 {
21311 foreach ($this->factories as $factory) {
21312 $command = $factory->factory($name, $args);
21313 if ($command) {
21314 return $command;
21315 }
21316 }
21317 }
21318
21319 public function count()
21320 {
21321 return count($this->factories);
21322 }
21323
21324 public function getIterator()
21325 {
21326 return new \ArrayIterator($this->factories);
21327 }
21328 }
21329 <?php
21330
21331 namespace Guzzle\Service\Command\Factory;
21332
21333 use Guzzle\Service\Description\ServiceDescriptionInterface;
21334 use Guzzle\Inflection\InflectorInterface;
21335
21336 /**
21337 * Command factory used to create commands based on service descriptions
21338 */
21339 class ServiceDescriptionFactory implements FactoryInterface
21340 {
21341 /** @var ServiceDescriptionInterface */
21342 protected $description;
21343
21344 /** @var InflectorInterface */
21345 protected $inflector;
21346
21347 /**
21348 * @param ServiceDescriptionInterface $description Service description
21349 * @param InflectorInterface $inflector Optional inflector to use if the command is not at first found
21350 */
21351 public function __construct(ServiceDescriptionInterface $description, InflectorInterface $inflector = null)
21352 {
21353 $this->setServiceDescription($description);
21354 $this->inflector = $inflector;
21355 }
21356
21357 /**
21358 * Change the service description used with the factory
21359 *
21360 * @param ServiceDescriptionInterface $description Service description to use
21361 *
21362 * @return FactoryInterface
21363 */
21364 public function setServiceDescription(ServiceDescriptionInterface $description)
21365 {
21366 $this->description = $description;
21367
21368 return $this;
21369 }
21370
21371 /**
21372 * Returns the service description
21373 *
21374 * @return ServiceDescriptionInterface
21375 */
21376 public function getServiceDescription()
21377 {
21378 return $this->description;
21379 }
21380
21381 public function factory($name, array $args = array())
21382 {
21383 $command = $this->description->getOperation($name);
21384
21385 // If a command wasn't found, then try to uppercase the first letter and try again
21386 if (!$command) {
21387 $command = $this->description->getOperation(ucfirst($name));
21388 // If an inflector was passed, then attempt to get the command using snake_case inflection
21389 if (!$command && $this->inflector) {
21390 $command = $this->description->getOperation($this->inflector->snake($name));
21391 }
21392 }
21393
21394 if ($command) {
21395 $class = $command->getClass();
21396 return new $class($args, $command, $this->description);
21397 }
21398 }
21399 }
21400 <?php
21401
21402 namespace Guzzle\Service\Command\Factory;
21403
21404 use Guzzle\Inflection\InflectorInterface;
21405 use Guzzle\Inflection\Inflector;
21406 use Guzzle\Service\ClientInterface;
21407
21408 /**
21409 * Command factory used to create commands referencing concrete command classes
21410 */
21411 class ConcreteClassFactory implements FactoryInterface
21412 {
21413 /** @var ClientInterface */
21414 protected $client;
21415
21416 /** @var InflectorInterface */
21417 protected $inflector;
21418
21419 /**
21420 * @param ClientInterface $client Client that owns the commands
21421 * @param InflectorInterface $inflector Inflector used to resolve class names
21422 */
21423 public function __construct(ClientInterface $client, InflectorInterface $inflector = null)
21424 {
21425 $this->client = $client;
21426 $this->inflector = $inflector ?: Inflector::getDefault();
21427 }
21428
21429 public function factory($name, array $args = array())
21430 {
21431 // Determine the class to instantiate based on the namespace of the current client and the default directory
21432 $prefix = $this->client->getConfig('command.prefix');
21433 if (!$prefix) {
21434 // The prefix can be specified in a factory method and is cached
21435 $prefix = implode('\\', array_slice(explode('\\', get_class($this->client)), 0, -1)) . '\\Command\\';
21436 $this->client->getConfig()->set('command.prefix', $prefix);
21437 }
21438
21439 $class = $prefix . str_replace(' ', '\\', ucwords(str_replace('.', ' ', $this->inflector->camel($name))));
21440
21441 // Create the concrete command if it exists
21442 if (class_exists($class)) {
21443 return new $class($args);
21444 }
21445 }
21446 }
21447 <?php
21448
21449 namespace Guzzle\Service\Command\Factory;
21450
21451 use Guzzle\Common\Exception\InvalidArgumentException;
21452 use Guzzle\Service\ClientInterface;
21453
21454 /**
21455 * Command factory used when you need to provide aliases to commands
21456 */
21457 class AliasFactory implements FactoryInterface
21458 {
21459 /** @var array Associative array mapping command aliases to the aliased command */
21460 protected $aliases;
21461
21462 /** @var ClientInterface Client used to retry using aliases */
21463 protected $client;
21464
21465 /**
21466 * @param ClientInterface $client Client used to retry with the alias
21467 * @param array $aliases Associative array mapping aliases to the alias
21468 */
21469 public function __construct(ClientInterface $client, array $aliases)
21470 {
21471 $this->client = $client;
21472 $this->aliases = $aliases;
21473 }
21474
21475 public function factory($name, array $args = array())
21476 {
21477 if (isset($this->aliases[$name])) {
21478 try {
21479 return $this->client->getCommand($this->aliases[$name], $args);
21480 } catch (InvalidArgumentException $e) {
21481 return null;
21482 }
21483 }
21484 }
21485 }
21486 <?php
21487
21488 namespace Guzzle\Service\Command\Factory;
21489
21490 use Guzzle\Service\Command\CommandInterface;
21491
21492 /**
21493 * Interface for creating commands by name
21494 */
21495 interface FactoryInterface
21496 {
21497 /**
21498 * Create a command by name
21499 *
21500 * @param string $name Command to create
21501 * @param array $args Command arguments
21502 *
21503 * @return CommandInterface|null
21504 */
21505 public function factory($name, array $args = array());
21506 }
21507 <?php
21508
21509 namespace Guzzle\Service\Command;
21510
21511 use Guzzle\Http\Message\Response;
21512
21513 /**
21514 * Default HTTP response parser used to marshal JSON responses into arrays and XML responses into SimpleXMLElement
21515 */
21516 class DefaultResponseParser implements ResponseParserInterface
21517 {
21518 /** @var self */
21519 protected static $instance;
21520
21521 /**
21522 * @return self
21523 * @codeCoverageIgnore
21524 */
21525 public static function getInstance()
21526 {
21527 if (!self::$instance) {
21528 self::$instance = new self;
21529 }
21530
21531 return self::$instance;
21532 }
21533
21534 public function parse(CommandInterface $command)
21535 {
21536 $response = $command->getRequest()->getResponse();
21537
21538 // Account for hard coded content-type values specified in service descriptions
21539 if ($contentType = $command['command.expects']) {
21540 $response->setHeader('Content-Type', $contentType);
21541 } else {
21542 $contentType = (string) $response->getHeader('Content-Type');
21543 }
21544
21545 return $this->handleParsing($command, $response, $contentType);
21546 }
21547
21548 protected function handleParsing(CommandInterface $command, Response $response, $contentType)
21549 {
21550 $result = $response;
21551 if ($result->getBody()) {
21552 if (stripos($contentType, 'json') !== false) {
21553 $result = $result->json();
21554 } if (stripos($contentType, 'xml') !== false) {
21555 $result = $result->xml();
21556 }
21557 }
21558
21559 return $result;
21560 }
21561 }
21562 <?php
21563
21564 namespace Guzzle\Service\Command;
21565
21566 use Guzzle\Common\Collection;
21567 use Guzzle\Common\Exception\InvalidArgumentException;
21568 use Guzzle\Http\Message\Response;
21569 use Guzzle\Http\Message\RequestInterface;
21570 use Guzzle\Service\Exception\CommandException;
21571 use Guzzle\Service\Description\OperationInterface;
21572 use Guzzle\Service\ClientInterface;
21573 use Guzzle\Common\ToArrayInterface;
21574
21575 /**
21576 * A command object that contains parameters that can be modified and accessed like an array and turned into an array
21577 */
21578 interface CommandInterface extends \ArrayAccess, ToArrayInterface
21579 {
21580 /**
21581 * Get the short form name of the command
21582 *
21583 * @return string
21584 */
21585 public function getName();
21586
21587 /**
21588 * Get the API operation information about the command
21589 *
21590 * @return OperationInterface
21591 */
21592 public function getOperation();
21593
21594 /**
21595 * Execute the command and return the result
21596 *
21597 * @return mixed Returns the result of {@see CommandInterface::execute}
21598 * @throws CommandException if a client has not been associated with the command
21599 */
21600 public function execute();
21601
21602 /**
21603 * Get the client object that will execute the command
21604 *
21605 * @return ClientInterface|null
21606 */
21607 public function getClient();
21608
21609 /**
21610 * Set the client object that will execute the command
21611 *
21612 * @param ClientInterface $client The client object that will execute the command
21613 *
21614 * @return self
21615 */
21616 public function setClient(ClientInterface $client);
21617
21618 /**
21619 * Get the request object associated with the command
21620 *
21621 * @return RequestInterface
21622 * @throws CommandException if the command has not been executed
21623 */
21624 public function getRequest();
21625
21626 /**
21627 * Get the response object associated with the command
21628 *
21629 * @return Response
21630 * @throws CommandException if the command has not been executed
21631 */
21632 public function getResponse();
21633
21634 /**
21635 * Get the result of the command
21636 *
21637 * @return Response By default, commands return a Response object unless overridden in a subclass
21638 * @throws CommandException if the command has not been executed
21639 */
21640 public function getResult();
21641
21642 /**
21643 * Set the result of the command
21644 *
21645 * @param mixed $result Result to set
21646 *
21647 * @return self
21648 */
21649 public function setResult($result);
21650
21651 /**
21652 * Returns TRUE if the command has been prepared for executing
21653 *
21654 * @return bool
21655 */
21656 public function isPrepared();
21657
21658 /**
21659 * Returns TRUE if the command has been executed
21660 *
21661 * @return bool
21662 */
21663 public function isExecuted();
21664
21665 /**
21666 * Prepare the command for executing and create a request object.
21667 *
21668 * @return RequestInterface Returns the generated request
21669 * @throws CommandException if a client object has not been set previously or in the prepare()
21670 */
21671 public function prepare();
21672
21673 /**
21674 * Get the object that manages the request headers that will be set on any outbound requests from the command
21675 *
21676 * @return Collection
21677 */
21678 public function getRequestHeaders();
21679
21680 /**
21681 * Specify a callable to execute when the command completes
21682 *
21683 * @param mixed $callable Callable to execute when the command completes. The callable must accept a
21684 * {@see CommandInterface} object as the only argument.
21685 * @return self
21686 * @throws InvalidArgumentException
21687 */
21688 public function setOnComplete($callable);
21689 }
21690 <?php
21691
21692 namespace Guzzle\Service\Command;
21693
21694 use Guzzle\Common\Collection;
21695 use Guzzle\Common\Exception\InvalidArgumentException;
21696 use Guzzle\Http\Message\Response;
21697 use Guzzle\Http\Message\RequestInterface;
21698 use Guzzle\Http\Curl\CurlHandle;
21699 use Guzzle\Service\Client;
21700 use Guzzle\Service\ClientInterface;
21701 use Guzzle\Service\Description\Operation;
21702 use Guzzle\Service\Description\OperationInterface;
21703 use Guzzle\Service\Description\ValidatorInterface;
21704 use Guzzle\Service\Description\SchemaValidator;
21705 use Guzzle\Service\Exception\CommandException;
21706 use Guzzle\Service\Exception\ValidationException;
21707
21708 /**
21709 * Command object to handle preparing and processing client requests and responses of the requests
21710 */
21711 abstract class AbstractCommand extends Collection implements CommandInterface
21712 {
21713 // @deprecated: Option used to specify custom headers to add to the generated request
21714 const HEADERS_OPTION = 'command.headers';
21715 // @deprecated: Option used to add an onComplete method to a command
21716 const ON_COMPLETE = 'command.on_complete';
21717 // @deprecated: Option used to change the entity body used to store a response
21718 const RESPONSE_BODY = 'command.response_body';
21719
21720 // Option used to add request options to the request created by a command
21721 const REQUEST_OPTIONS = 'command.request_options';
21722 // command values to not count as additionalParameters
21723 const HIDDEN_PARAMS = 'command.hidden_params';
21724 // Option used to disable any pre-sending command validation
21725 const DISABLE_VALIDATION = 'command.disable_validation';
21726 // Option used to override how a command result will be formatted
21727 const RESPONSE_PROCESSING = 'command.response_processing';
21728 // Different response types that commands can use
21729 const TYPE_RAW = 'raw';
21730 const TYPE_MODEL = 'model';
21731 const TYPE_NO_TRANSLATION = 'no_translation';
21732
21733 /** @var ClientInterface Client object used to execute the command */
21734 protected $client;
21735
21736 /** @var RequestInterface The request object associated with the command */
21737 protected $request;
21738
21739 /** @var mixed The result of the command */
21740 protected $result;
21741
21742 /** @var OperationInterface API information about the command */
21743 protected $operation;
21744
21745 /** @var mixed callable */
21746 protected $onComplete;
21747
21748 /** @var ValidatorInterface Validator used to prepare and validate properties against a JSON schema */
21749 protected $validator;
21750
21751 /**
21752 * @param array|Collection $parameters Collection of parameters to set on the command
21753 * @param OperationInterface $operation Command definition from description
21754 */
21755 public function __construct($parameters = array(), OperationInterface $operation = null)
21756 {
21757 parent::__construct($parameters);
21758 $this->operation = $operation ?: $this->createOperation();
21759 foreach ($this->operation->getParams() as $name => $arg) {
21760 $currentValue = $this[$name];
21761 $configValue = $arg->getValue($currentValue);
21762 // If default or static values are set, then this should always be updated on the config object
21763 if ($currentValue !== $configValue) {
21764 $this[$name] = $configValue;
21765 }
21766 }
21767
21768 $headers = $this[self::HEADERS_OPTION];
21769 if (!$headers instanceof Collection) {
21770 $this[self::HEADERS_OPTION] = new Collection((array) $headers);
21771 }
21772
21773 // You can set a command.on_complete option in your parameters to set an onComplete callback
21774 if ($onComplete = $this['command.on_complete']) {
21775 unset($this['command.on_complete']);
21776 $this->setOnComplete($onComplete);
21777 }
21778
21779 // Set the hidden additional parameters
21780 if (!$this[self::HIDDEN_PARAMS]) {
21781 $this[self::HIDDEN_PARAMS] = array(
21782 self::HEADERS_OPTION,
21783 self::RESPONSE_PROCESSING,
21784 self::HIDDEN_PARAMS,
21785 self::REQUEST_OPTIONS
21786 );
21787 }
21788
21789 $this->init();
21790 }
21791
21792 /**
21793 * Custom clone behavior
21794 */
21795 public function __clone()
21796 {
21797 $this->request = null;
21798 $this->result = null;
21799 }
21800
21801 /**
21802 * Execute the command in the same manner as calling a function
21803 *
21804 * @return mixed Returns the result of {@see AbstractCommand::execute}
21805 */
21806 public function __invoke()
21807 {
21808 return $this->execute();
21809 }
21810
21811 public function getName()
21812 {
21813 return $this->operation->getName();
21814 }
21815
21816 /**
21817 * Get the API command information about the command
21818 *
21819 * @return OperationInterface
21820 */
21821 public function getOperation()
21822 {
21823 return $this->operation;
21824 }
21825
21826 public function setOnComplete($callable)
21827 {
21828 if (!is_callable($callable)) {
21829 throw new InvalidArgumentException('The onComplete function must be callable');
21830 }
21831
21832 $this->onComplete = $callable;
21833
21834 return $this;
21835 }
21836
21837 public function execute()
21838 {
21839 if (!$this->client) {
21840 throw new CommandException('A client must be associated with the command before it can be executed.');
21841 }
21842
21843 return $this->client->execute($this);
21844 }
21845
21846 public function getClient()
21847 {
21848 return $this->client;
21849 }
21850
21851 public function setClient(ClientInterface $client)
21852 {
21853 $this->client = $client;
21854
21855 return $this;
21856 }
21857
21858 public function getRequest()
21859 {
21860 if (!$this->request) {
21861 throw new CommandException('The command must be prepared before retrieving the request');
21862 }
21863
21864 return $this->request;
21865 }
21866
21867 public function getResponse()
21868 {
21869 if (!$this->isExecuted()) {
21870 $this->execute();
21871 }
21872
21873 return $this->request->getResponse();
21874 }
21875
21876 public function getResult()
21877 {
21878 if (!$this->isExecuted()) {
21879 $this->execute();
21880 }
21881
21882 if (null === $this->result) {
21883 $this->process();
21884 // Call the onComplete method if one is set
21885 if ($this->onComplete) {
21886 call_user_func($this->onComplete, $this);
21887 }
21888 }
21889
21890 return $this->result;
21891 }
21892
21893 public function setResult($result)
21894 {
21895 $this->result = $result;
21896
21897 return $this;
21898 }
21899
21900 public function isPrepared()
21901 {
21902 return $this->request !== null;
21903 }
21904
21905 public function isExecuted()
21906 {
21907 return $this->request !== null && $this->request->getState() == 'complete';
21908 }
21909
21910 public function prepare()
21911 {
21912 if (!$this->isPrepared()) {
21913 if (!$this->client) {
21914 throw new CommandException('A client must be associated with the command before it can be prepared.');
21915 }
21916
21917 // If no response processing value was specified, then attempt to use the highest level of processing
21918 if (!isset($this[self::RESPONSE_PROCESSING])) {
21919 $this[self::RESPONSE_PROCESSING] = self::TYPE_MODEL;
21920 }
21921
21922 // Notify subscribers of the client that the command is being prepared
21923 $this->client->dispatch('command.before_prepare', array('command' => $this));
21924
21925 // Fail on missing required arguments, and change parameters via filters
21926 $this->validate();
21927 // Delegate to the subclass that implements the build method
21928 $this->build();
21929
21930 // Add custom request headers set on the command
21931 if ($headers = $this[self::HEADERS_OPTION]) {
21932 foreach ($headers as $key => $value) {
21933 $this->request->setHeader($key, $value);
21934 }
21935 }
21936
21937 // Add any curl options to the request
21938 if ($options = $this[Client::CURL_OPTIONS]) {
21939 $this->request->getCurlOptions()->overwriteWith(CurlHandle::parseCurlConfig($options));
21940 }
21941
21942 // Set a custom response body
21943 if ($responseBody = $this[self::RESPONSE_BODY]) {
21944 $this->request->setResponseBody($responseBody);
21945 }
21946
21947 $this->client->dispatch('command.after_prepare', array('command' => $this));
21948 }
21949
21950 return $this->request;
21951 }
21952
21953 /**
21954 * Set the validator used to validate and prepare command parameters and nested JSON schemas. If no validator is
21955 * set, then the command will validate using the default {@see SchemaValidator}.
21956 *
21957 * @param ValidatorInterface $validator Validator used to prepare and validate properties against a JSON schema
21958 *
21959 * @return self
21960 */
21961 public function setValidator(ValidatorInterface $validator)
21962 {
21963 $this->validator = $validator;
21964
21965 return $this;
21966 }
21967
21968 public function getRequestHeaders()
21969 {
21970 return $this[self::HEADERS_OPTION];
21971 }
21972
21973 /**
21974 * Initialize the command (hook that can be implemented in subclasses)
21975 */
21976 protected function init() {}
21977
21978 /**
21979 * Create the request object that will carry out the command
21980 */
21981 abstract protected function build();
21982
21983 /**
21984 * Hook used to create an operation for concrete commands that are not associated with a service description
21985 *
21986 * @return OperationInterface
21987 */
21988 protected function createOperation()
21989 {
21990 return new Operation(array('name' => get_class($this)));
21991 }
21992
21993 /**
21994 * Create the result of the command after the request has been completed.
21995 * Override this method in subclasses to customize this behavior
21996 */
21997 protected function process()
21998 {
21999 $this->result = $this[self::RESPONSE_PROCESSING] != self::TYPE_RAW
22000 ? DefaultResponseParser::getInstance()->parse($this)
22001 : $this->request->getResponse();
22002 }
22003
22004 /**
22005 * Validate and prepare the command based on the schema and rules defined by the command's Operation object
22006 *
22007 * @throws ValidationException when validation errors occur
22008 */
22009 protected function validate()
22010 {
22011 // Do not perform request validation/transformation if it is disable
22012 if ($this[self::DISABLE_VALIDATION]) {
22013 return;
22014 }
22015
22016 $errors = array();
22017 $validator = $this->getValidator();
22018 foreach ($this->operation->getParams() as $name => $schema) {
22019 $value = $this[$name];
22020 if (!$validator->validate($schema, $value)) {
22021 $errors = array_merge($errors, $validator->getErrors());
22022 } elseif ($value !== $this[$name]) {
22023 // Update the config value if it changed and no validation errors were encountered
22024 $this->data[$name] = $value;
22025 }
22026 }
22027
22028 // Validate additional parameters
22029 $hidden = $this[self::HIDDEN_PARAMS];
22030
22031 if ($properties = $this->operation->getAdditionalParameters()) {
22032 foreach ($this->toArray() as $name => $value) {
22033 // It's only additional if it isn't defined in the schema
22034 if (!$this->operation->hasParam($name) && !in_array($name, $hidden)) {
22035 // Always set the name so that error messages are useful
22036 $properties->setName($name);
22037 if (!$validator->validate($properties, $value)) {
22038 $errors = array_merge($errors, $validator->getErrors());
22039 } elseif ($value !== $this[$name]) {
22040 $this->data[$name] = $value;
22041 }
22042 }
22043 }
22044 }
22045
22046 if (!empty($errors)) {
22047 $e = new ValidationException('Validation errors: ' . implode("\n", $errors));
22048 $e->setErrors($errors);
22049 throw $e;
22050 }
22051 }
22052
22053 /**
22054 * Get the validator used to prepare and validate properties. If no validator has been set on the command, then
22055 * the default {@see SchemaValidator} will be used.
22056 *
22057 * @return ValidatorInterface
22058 */
22059 protected function getValidator()
22060 {
22061 if (!$this->validator) {
22062 $this->validator = SchemaValidator::getInstance();
22063 }
22064
22065 return $this->validator;
22066 }
22067 }
22068 <?php
22069
22070 namespace Guzzle\Service\Command;
22071
22072 /**
22073 * A command that creates requests based on {@see Guzzle\Service\Description\OperationInterface} objects, and if the
22074 * matching operation uses a service description model in the responseClass attribute, then this command will marshal
22075 * the response into an associative array based on the JSON schema of the model.
22076 */
22077 class OperationCommand extends AbstractCommand
22078 {
22079 /** @var RequestSerializerInterface */
22080 protected $requestSerializer;
22081
22082 /** @var ResponseParserInterface Response parser */
22083 protected $responseParser;
22084
22085 /**
22086 * Set the response parser used with the command
22087 *
22088 * @param ResponseParserInterface $parser Response parser
22089 *
22090 * @return self
22091 */
22092 public function setResponseParser(ResponseParserInterface $parser)
22093 {
22094 $this->responseParser = $parser;
22095
22096 return $this;
22097 }
22098
22099 /**
22100 * Set the request serializer used with the command
22101 *
22102 * @param RequestSerializerInterface $serializer Request serializer
22103 *
22104 * @return self
22105 */
22106 public function setRequestSerializer(RequestSerializerInterface $serializer)
22107 {
22108 $this->requestSerializer = $serializer;
22109
22110 return $this;
22111 }
22112
22113 /**
22114 * Get the request serializer used with the command
22115 *
22116 * @return RequestSerializerInterface
22117 */
22118 public function getRequestSerializer()
22119 {
22120 if (!$this->requestSerializer) {
22121 // Use the default request serializer if none was found
22122 $this->requestSerializer = DefaultRequestSerializer::getInstance();
22123 }
22124
22125 return $this->requestSerializer;
22126 }
22127
22128 /**
22129 * Get the response parser used for the operation
22130 *
22131 * @return ResponseParserInterface
22132 */
22133 public function getResponseParser()
22134 {
22135 if (!$this->responseParser) {
22136 // Use the default response parser if none was found
22137 $this->responseParser = OperationResponseParser::getInstance();
22138 }
22139
22140 return $this->responseParser;
22141 }
22142
22143 protected function build()
22144 {
22145 // Prepare and serialize the request
22146 $this->request = $this->getRequestSerializer()->prepare($this);
22147 }
22148
22149 protected function process()
22150 {
22151 // Do not process the response if 'command.response_processing' is set to 'raw'
22152 $this->result = $this[self::RESPONSE_PROCESSING] == self::TYPE_RAW
22153 ? $this->request->getResponse()
22154 : $this->getResponseParser()->parse($this);
22155 }
22156 }
22157 <?php
22158
22159 namespace Guzzle\Service\Command;
22160
22161 use Guzzle\Http\Message\RequestInterface;
22162 use Guzzle\Service\Command\CommandInterface;
22163
22164 /**
22165 * Translates command options and operation parameters into a request object
22166 */
22167 interface RequestSerializerInterface
22168 {
22169 /**
22170 * Create a request for a command
22171 *
22172 * @param CommandInterface $command Command that will own the request
22173 *
22174 * @return RequestInterface
22175 */
22176 public function prepare(CommandInterface $command);
22177 }
22178 <?php
22179
22180 namespace Guzzle\Service\Command\LocationVisitor\Request;
22181
22182 use Guzzle\Http\Message\RequestInterface;
22183 use Guzzle\Service\Command\CommandInterface;
22184 use Guzzle\Service\Description\Operation;
22185 use Guzzle\Service\Description\Parameter;
22186
22187 /**
22188 * Location visitor used to serialize XML bodies
22189 */
22190 class XmlVisitor extends AbstractRequestVisitor
22191 {
22192 /** @var \SplObjectStorage Data object for persisting XML data */
22193 protected $data;
22194
22195 /** @var \XMLWriter XML writer resource */
22196 protected $writer;
22197
22198 /** @var bool Content-Type header added when XML is found */
22199 protected $contentType = 'application/xml';
22200
22201 public function __construct()
22202 {
22203 $this->data = new \SplObjectStorage();
22204 }
22205
22206 /**
22207 * Change the content-type header that is added when XML is found
22208 *
22209 * @param string $header Header to set when XML is found
22210 *
22211 * @return self
22212 */
22213 public function setContentTypeHeader($header)
22214 {
22215 $this->contentType = $header;
22216
22217 return $this;
22218 }
22219
22220 public function visit(CommandInterface $command, RequestInterface $request, Parameter $param, $value)
22221 {
22222 $xml = isset($this->data[$command])
22223 ? $this->data[$command]
22224 : $this->createRootElement($param->getParent());
22225 $this->addXml($xml, $param, $value);
22226
22227 $this->data[$command] = $xml;
22228 }
22229
22230 public function after(CommandInterface $command, RequestInterface $request)
22231 {
22232 $xml = null;
22233
22234 // If data was found that needs to be serialized, then do so
22235 if (isset($this->data[$command])) {
22236 $xml = $this->finishDocument($this->writer);
22237 unset($this->data[$command]);
22238 } else {
22239 // Check if XML should always be sent for the command
22240 $operation = $command->getOperation();
22241 if ($operation->getData('xmlAllowEmpty')) {
22242 $xmlWriter = $this->createRootElement($operation);
22243 $xml = $this->finishDocument($xmlWriter);
22244 }
22245 }
22246
22247 if ($xml) {
22248 // Don't overwrite the Content-Type if one is set
22249 if ($this->contentType && !$request->hasHeader('Content-Type')) {
22250 $request->setHeader('Content-Type', $this->contentType);
22251 }
22252 $request->setBody($xml);
22253 }
22254 }
22255
22256 /**
22257 * Create the root XML element to use with a request
22258 *
22259 * @param Operation $operation Operation object
22260 *
22261 * @return \XMLWriter
22262 */
22263 protected function createRootElement(Operation $operation)
22264 {
22265 static $defaultRoot = array('name' => 'Request');
22266 // If no root element was specified, then just wrap the XML in 'Request'
22267 $root = $operation->getData('xmlRoot') ?: $defaultRoot;
22268 // Allow the XML declaration to be customized with xmlEncoding
22269 $encoding = $operation->getData('xmlEncoding');
22270
22271 $xmlWriter = $this->startDocument($encoding);
22272
22273 $xmlWriter->startElement($root['name']);
22274 // Create the wrapping element with no namespaces if no namespaces were present
22275 if (!empty($root['namespaces'])) {
22276 // Create the wrapping element with an array of one or more namespaces
22277 foreach ((array) $root['namespaces'] as $prefix => $uri) {
22278 $nsLabel = 'xmlns';
22279 if (!is_numeric($prefix)) {
22280 $nsLabel .= ':'.$prefix;
22281 }
22282 $xmlWriter->writeAttribute($nsLabel, $uri);
22283 }
22284 }
22285 return $xmlWriter;
22286 }
22287
22288 /**
22289 * Recursively build the XML body
22290 *
22291 * @param \XMLWriter $xmlWriter XML to modify
22292 * @param Parameter $param API Parameter
22293 * @param mixed $value Value to add
22294 */
22295 protected function addXml(\XMLWriter $xmlWriter, Parameter $param, $value)
22296 {
22297 if ($value === null) {
22298 return;
22299 }
22300
22301 $value = $param->filter($value);
22302 $type = $param->getType();
22303 $name = $param->getWireName();
22304 $prefix = null;
22305 $namespace = $param->getData('xmlNamespace');
22306 if (false !== strpos($name, ':')) {
22307 list($prefix, $name) = explode(':', $name, 2);
22308 }
22309
22310 if ($type == 'object' || $type == 'array') {
22311 if (!$param->getData('xmlFlattened')) {
22312 $xmlWriter->startElementNS(null, $name, $namespace);
22313 }
22314 if ($param->getType() == 'array') {
22315 $this->addXmlArray($xmlWriter, $param, $value);
22316 } elseif ($param->getType() == 'object') {
22317 $this->addXmlObject($xmlWriter, $param, $value);
22318 }
22319 if (!$param->getData('xmlFlattened')) {
22320 $xmlWriter->endElement();
22321 }
22322 return;
22323 }
22324 if ($param->getData('xmlAttribute')) {
22325 $this->writeAttribute($xmlWriter, $prefix, $name, $namespace, $value);
22326 } else {
22327 $this->writeElement($xmlWriter, $prefix, $name, $namespace, $value);
22328 }
22329 }
22330
22331 /**
22332 * Write an attribute with namespace if used
22333 *
22334 * @param \XMLWriter $xmlWriter XMLWriter instance
22335 * @param string $prefix Namespace prefix if any
22336 * @param string $name Attribute name
22337 * @param string $namespace The uri of the namespace
22338 * @param string $value The attribute content
22339 */
22340 protected function writeAttribute($xmlWriter, $prefix, $name, $namespace, $value)
22341 {
22342 if (empty($namespace)) {
22343 $xmlWriter->writeAttribute($name, $value);
22344 } else {
22345 $xmlWriter->writeAttributeNS($prefix, $name, $namespace, $value);
22346 }
22347 }
22348
22349 /**
22350 * Write an element with namespace if used
22351 *
22352 * @param \XMLWriter $xmlWriter XML writer resource
22353 * @param string $prefix Namespace prefix if any
22354 * @param string $name Element name
22355 * @param string $namespace The uri of the namespace
22356 * @param string $value The element content
22357 */
22358 protected function writeElement(\XMLWriter $xmlWriter, $prefix, $name, $namespace, $value)
22359 {
22360 $xmlWriter->startElementNS($prefix, $name, $namespace);
22361 if (strpbrk($value, '<>&')) {
22362 $xmlWriter->writeCData($value);
22363 } else {
22364 $xmlWriter->writeRaw($value);
22365 }
22366 $xmlWriter->endElement();
22367 }
22368
22369 /**
22370 * Create a new xml writier and start a document
22371 *
22372 * @param string $encoding document encoding
22373 *
22374 * @return \XMLWriter the writer resource
22375 */
22376 protected function startDocument($encoding)
22377 {
22378 $this->writer = new \XMLWriter();
22379 $this->writer->openMemory();
22380 $this->writer->startDocument('1.0', $encoding);
22381
22382 return $this->writer;
22383 }
22384
22385 /**
22386 * End the document and return the output
22387 *
22388 * @param \XMLWriter $xmlWriter
22389 *
22390 * @return \string the writer resource
22391 */
22392 protected function finishDocument($xmlWriter)
22393 {
22394 $xmlWriter->endDocument();
22395
22396 return $xmlWriter->outputMemory();
22397 }
22398
22399 /**
22400 * Add an array to the XML
22401 */
22402 protected function addXmlArray(\XMLWriter $xmlWriter, Parameter $param, &$value)
22403 {
22404 if ($items = $param->getItems()) {
22405 foreach ($value as $v) {
22406 $this->addXml($xmlWriter, $items, $v);
22407 }
22408 }
22409 }
22410
22411 /**
22412 * Add an object to the XML
22413 */
22414 protected function addXmlObject(\XMLWriter $xmlWriter, Parameter $param, &$value)
22415 {
22416 $noAttributes = array();
22417 // add values which have attriutes
22418 foreach ($value as $name => $v) {
22419 if ($property = $param->getProperty($name)) {
22420 if ($property->getData('xmlAttribute')) {
22421 $this->addXml($xmlWriter, $property, $v);
22422 } else {
22423 $noAttributes[] = array('value' => $v, 'property' => $property);
22424 }
22425 }
22426 }
22427 // now add values with no attributes
22428 foreach ($noAttributes as $element) {
22429 $this->addXml($xmlWriter, $element['property'], $element['value']);
22430 }
22431 }
22432 }
22433 <?php
22434
22435 namespace Guzzle\Service\Command\LocationVisitor\Request;
22436
22437 use Guzzle\Http\Message\RequestInterface;
22438 use Guzzle\Service\Description\Parameter;
22439 use Guzzle\Service\Command\CommandInterface;
22440
22441 /**
22442 * Location visitor used to add values to different locations in a request with different behaviors as needed
22443 */
22444 interface RequestVisitorInterface
22445 {
22446 /**
22447 * Called after visiting all parameters
22448 *
22449 * @param CommandInterface $command Command being visited
22450 * @param RequestInterface $request Request being visited
22451 */
22452 public function after(CommandInterface $command, RequestInterface $request);
22453
22454 /**
22455 * Called once for each parameter being visited that matches the location type
22456 *
22457 * @param CommandInterface $command Command being visited
22458 * @param RequestInterface $request Request being visited
22459 * @param Parameter $param Parameter being visited
22460 * @param mixed $value Value to set
22461 */
22462 public function visit(CommandInterface $command, RequestInterface $request, Parameter $param, $value);
22463 }
22464 <?php
22465
22466 namespace Guzzle\Service\Command\LocationVisitor\Request;
22467
22468 use Guzzle\Http\Message\RequestInterface;
22469 use Guzzle\Service\Command\CommandInterface;
22470 use Guzzle\Service\Description\Parameter;
22471
22472 /**
22473 * Visitor used to apply a parameter to a POST field
22474 */
22475 class PostFieldVisitor extends AbstractRequestVisitor
22476 {
22477 public function visit(CommandInterface $command, RequestInterface $request, Parameter $param, $value)
22478 {
22479 $request->setPostField($param->getWireName(), $this->prepareValue($value, $param));
22480 }
22481 }
22482 <?php
22483
22484 namespace Guzzle\Service\Command\LocationVisitor\Request;
22485
22486 use Guzzle\Http\EntityBody;
22487 use Guzzle\Http\Message\EntityEnclosingRequestInterface;
22488 use Guzzle\Http\Message\RequestInterface;
22489 use Guzzle\Http\EntityBodyInterface;
22490 use Guzzle\Service\Command\CommandInterface;
22491 use Guzzle\Service\Description\Parameter;
22492
22493 /**
22494 * Visitor used to apply a body to a request
22495 *
22496 * This visitor can use a data parameter of 'expect' to control the Expect header. Set the expect data parameter to
22497 * false to disable the expect header, or set the value to an integer so that the expect 100-continue header is only
22498 * added if the Content-Length of the entity body is greater than the value.
22499 */
22500 class BodyVisitor extends AbstractRequestVisitor
22501 {
22502 public function visit(CommandInterface $command, RequestInterface $request, Parameter $param, $value)
22503 {
22504 $value = $param->filter($value);
22505 $entityBody = EntityBody::factory($value);
22506 $request->setBody($entityBody);
22507 $this->addExpectHeader($request, $entityBody, $param->getData('expect_header'));
22508 // Add the Content-Encoding header if one is set on the EntityBody
22509 if ($encoding = $entityBody->getContentEncoding()) {
22510 $request->setHeader('Content-Encoding', $encoding);
22511 }
22512 }
22513
22514 /**
22515 * Add the appropriate expect header to a request
22516 *
22517 * @param EntityEnclosingRequestInterface $request Request to update
22518 * @param EntityBodyInterface $body Entity body of the request
22519 * @param string|int $expect Expect header setting
22520 */
22521 protected function addExpectHeader(EntityEnclosingRequestInterface $request, EntityBodyInterface $body, $expect)
22522 {
22523 // Allow the `expect` data parameter to be set to remove the Expect header from the request
22524 if ($expect === false) {
22525 $request->removeHeader('Expect');
22526 } elseif ($expect !== true) {
22527 // Default to using a MB as the point in which to start using the expect header
22528 $expect = $expect ?: 1048576;
22529 // If the expect_header value is numeric then only add if the size is greater than the cutoff
22530 if (is_numeric($expect) && $body->getSize()) {
22531 if ($body->getSize() < $expect) {
22532 $request->removeHeader('Expect');
22533 } else {
22534 $request->setHeader('Expect', '100-Continue');
22535 }
22536 }
22537 }
22538 }
22539 }
22540 <?php
22541
22542 namespace Guzzle\Service\Command\LocationVisitor\Request;
22543
22544 use Guzzle\Http\Message\RequestInterface;
22545 use Guzzle\Http\Message\PostFileInterface;
22546 use Guzzle\Service\Command\CommandInterface;
22547 use Guzzle\Service\Description\Parameter;
22548
22549 /**
22550 * Visitor used to apply a parameter to a POST file
22551 */
22552 class PostFileVisitor extends AbstractRequestVisitor
22553 {
22554 public function visit(CommandInterface $command, RequestInterface $request, Parameter $param, $value)
22555 {
22556 $value = $param->filter($value);
22557 if ($value instanceof PostFileInterface) {
22558 $request->addPostFile($value);
22559 } else {
22560 $request->addPostFile($param->getWireName(), $value);
22561 }
22562 }
22563 }
22564 <?php
22565
22566 namespace Guzzle\Service\Command\LocationVisitor\Request;
22567
22568 use Guzzle\Http\Message\RequestInterface;
22569 use Guzzle\Service\Command\CommandInterface;
22570 use Guzzle\Service\Description\Parameter;
22571
22572 /**
22573 * Visitor used to apply a parameter to a request's query string
22574 */
22575 class QueryVisitor extends AbstractRequestVisitor
22576 {
22577 public function visit(CommandInterface $command, RequestInterface $request, Parameter $param, $value)
22578 {
22579 $request->getQuery()->set($param->getWireName(), $this->prepareValue($value, $param));
22580 }
22581 }
22582 <?php
22583
22584 namespace Guzzle\Service\Command\LocationVisitor\Request;
22585
22586 use Guzzle\Service\Command\CommandInterface;
22587 use Guzzle\Http\Message\RequestInterface;
22588 use Guzzle\Service\Description\Parameter;
22589
22590 abstract class AbstractRequestVisitor implements RequestVisitorInterface
22591 {
22592 /**
22593 * @codeCoverageIgnore
22594 */
22595 public function after(CommandInterface $command, RequestInterface $request) {}
22596
22597 /**
22598 * @codeCoverageIgnore
22599 */
22600 public function visit(CommandInterface $command, RequestInterface $request, Parameter $param, $value) {}
22601
22602 /**
22603 * Prepare (filter and set desired name for request item) the value for request.
22604 *
22605 * @param mixed $value
22606 * @param \Guzzle\Service\Description\Parameter $param
22607 *
22608 * @return array|mixed
22609 */
22610 protected function prepareValue($value, Parameter $param)
22611 {
22612 return is_array($value)
22613 ? $this->resolveRecursively($value, $param)
22614 : $param->filter($value);
22615 }
22616
22617 /**
22618 * Map nested parameters into the location_key based parameters
22619 *
22620 * @param array $value Value to map
22621 * @param Parameter $param Parameter that holds information about the current key
22622 *
22623 * @return array Returns the mapped array
22624 */
22625 protected function resolveRecursively(array $value, Parameter $param)
22626 {
22627 foreach ($value as $name => &$v) {
22628 switch ($param->getType()) {
22629 case 'object':
22630 if ($subParam = $param->getProperty($name)) {
22631 $key = $subParam->getWireName();
22632 $value[$key] = $this->prepareValue($v, $subParam);
22633 if ($name != $key) {
22634 unset($value[$name]);
22635 }
22636 } elseif ($param->getAdditionalProperties() instanceof Parameter) {
22637 $v = $this->prepareValue($v, $param->getAdditionalProperties());
22638 }
22639 break;
22640 case 'array':
22641 if ($items = $param->getItems()) {
22642 $v = $this->prepareValue($v, $items);
22643 }
22644 break;
22645 }
22646 }
22647
22648 return $param->filter($value);
22649 }
22650 }
22651 <?php
22652
22653 namespace Guzzle\Service\Command\LocationVisitor\Request;
22654
22655 use Guzzle\Http\Message\RequestInterface;
22656 use Guzzle\Service\Command\CommandInterface;
22657 use Guzzle\Service\Description\Parameter;
22658
22659 /**
22660 * Visitor used to apply a parameter to an array that will be serialized as a top level key-value pair in a JSON body
22661 */
22662 class JsonVisitor extends AbstractRequestVisitor
22663 {
22664 /** @var bool Whether or not to add a Content-Type header when JSON is found */
22665 protected $jsonContentType = 'application/json';
22666
22667 /** @var \SplObjectStorage Data object for persisting JSON data */
22668 protected $data;
22669
22670 public function __construct()
22671 {
22672 $this->data = new \SplObjectStorage();
22673 }
22674
22675 /**
22676 * Set the Content-Type header to add to the request if JSON is added to the body. This visitor does not add a
22677 * Content-Type header unless you specify one here.
22678 *
22679 * @param string $header Header to set when JSON is added (e.g. application/json)
22680 *
22681 * @return self
22682 */
22683 public function setContentTypeHeader($header = 'application/json')
22684 {
22685 $this->jsonContentType = $header;
22686
22687 return $this;
22688 }
22689
22690 public function visit(CommandInterface $command, RequestInterface $request, Parameter $param, $value)
22691 {
22692 if (isset($this->data[$command])) {
22693 $json = $this->data[$command];
22694 } else {
22695 $json = array();
22696 }
22697 $json[$param->getWireName()] = $this->prepareValue($value, $param);
22698 $this->data[$command] = $json;
22699 }
22700
22701 public function after(CommandInterface $command, RequestInterface $request)
22702 {
22703 if (isset($this->data[$command])) {
22704 // Don't overwrite the Content-Type if one is set
22705 if ($this->jsonContentType && !$request->hasHeader('Content-Type')) {
22706 $request->setHeader('Content-Type', $this->jsonContentType);
22707 }
22708
22709 $request->setBody(json_encode($this->data[$command]));
22710 unset($this->data[$command]);
22711 }
22712 }
22713 }
22714 <?php
22715
22716 namespace Guzzle\Service\Command\LocationVisitor\Request;
22717
22718 use Guzzle\Common\Exception\InvalidArgumentException;
22719 use Guzzle\Http\Message\RequestInterface;
22720 use Guzzle\Service\Command\CommandInterface;
22721 use Guzzle\Service\Description\Parameter;
22722
22723 /**
22724 * Visitor used to apply a parameter to a header value
22725 */
22726 class HeaderVisitor extends AbstractRequestVisitor
22727 {
22728 public function visit(CommandInterface $command, RequestInterface $request, Parameter $param, $value)
22729 {
22730 $value = $param->filter($value);
22731 if ($param->getType() == 'object' && $param->getAdditionalProperties() instanceof Parameter) {
22732 $this->addPrefixedHeaders($request, $param, $value);
22733 } else {
22734 $request->setHeader($param->getWireName(), $value);
22735 }
22736 }
22737
22738 /**
22739 * Add a prefixed array of headers to the request
22740 *
22741 * @param RequestInterface $request Request to update
22742 * @param Parameter $param Parameter object
22743 * @param array $value Header array to add
22744 *
22745 * @throws InvalidArgumentException
22746 */
22747 protected function addPrefixedHeaders(RequestInterface $request, Parameter $param, $value)
22748 {
22749 if (!is_array($value)) {
22750 throw new InvalidArgumentException('An array of mapped headers expected, but received a single value');
22751 }
22752 $prefix = $param->getSentAs();
22753 foreach ($value as $headerName => $headerValue) {
22754 $request->setHeader($prefix . $headerName, $headerValue);
22755 }
22756 }
22757 }
22758 <?php
22759
22760 namespace Guzzle\Service\Command\LocationVisitor\Request;
22761
22762 use Guzzle\Http\Message\RequestInterface;
22763 use Guzzle\Service\Command\CommandInterface;
22764 use Guzzle\Service\Description\Parameter;
22765
22766 /**
22767 * Visitor used to change the location in which a response body is saved
22768 */
22769 class ResponseBodyVisitor extends AbstractRequestVisitor
22770 {
22771 public function visit(CommandInterface $command, RequestInterface $request, Parameter $param, $value)
22772 {
22773 $request->setResponseBody($value);
22774 }
22775 }
22776 <?php
22777
22778 namespace Guzzle\Service\Command\LocationVisitor\Response;
22779
22780 use Guzzle\Http\Message\Response;
22781 use Guzzle\Service\Description\Parameter;
22782 use Guzzle\Service\Command\CommandInterface;
22783
22784 /**
22785 * Location visitor used to marshal XML response data into a formatted array
22786 */
22787 class XmlVisitor extends AbstractResponseVisitor
22788 {
22789 public function before(CommandInterface $command, array &$result)
22790 {
22791 // Set the result of the command to the array conversion of the XML body
22792 $result = json_decode(json_encode($command->getResponse()->xml()), true);
22793 }
22794
22795 public function visit(
22796 CommandInterface $command,
22797 Response $response,
22798 Parameter $param,
22799 &$value,
22800 $context = null
22801 ) {
22802 $sentAs = $param->getWireName();
22803 $name = $param->getName();
22804 if (isset($value[$sentAs])) {
22805 $this->recursiveProcess($param, $value[$sentAs]);
22806 if ($name != $sentAs) {
22807 $value[$name] = $value[$sentAs];
22808 unset($value[$sentAs]);
22809 }
22810 }
22811 }
22812
22813 /**
22814 * Recursively process a parameter while applying filters
22815 *
22816 * @param Parameter $param API parameter being processed
22817 * @param mixed $value Value to validate and process. The value may change during this process.
22818 */
22819 protected function recursiveProcess(Parameter $param, &$value)
22820 {
22821 $type = $param->getType();
22822
22823 if (!is_array($value)) {
22824 if ($type == 'array') {
22825 // Cast to an array if the value was a string, but should be an array
22826 $this->recursiveProcess($param->getItems(), $value);
22827 $value = array($value);
22828 }
22829 } elseif ($type == 'object') {
22830 $this->processObject($param, $value);
22831 } elseif ($type == 'array') {
22832 $this->processArray($param, $value);
22833 }
22834
22835 if ($value !== null) {
22836 $value = $param->filter($value);
22837 }
22838 }
22839
22840 /**
22841 * Process an array
22842 *
22843 * @param Parameter $param API parameter being parsed
22844 * @param mixed $value Value to process
22845 */
22846 protected function processArray(Parameter $param, &$value)
22847 {
22848 // Convert the node if it was meant to be an array
22849 if (!isset($value[0])) {
22850 // Collections fo nodes are sometimes wrapped in an additional array. For example:
22851 // <Items><Item><a>1</a></Item><Item><a>2</a></Item></Items> should become:
22852 // array('Items' => array(array('a' => 1), array('a' => 2))
22853 // Some nodes are not wrapped. For example: <Foo><a>1</a></Foo><Foo><a>2</a></Foo>
22854 // should become array('Foo' => array(array('a' => 1), array('a' => 2))
22855 if ($param->getItems() && isset($value[$param->getItems()->getWireName()])) {
22856 // Account for the case of a collection wrapping wrapped nodes: Items => Item[]
22857 $value = $value[$param->getItems()->getWireName()];
22858 // If the wrapped node only had one value, then make it an array of nodes
22859 if (!isset($value[0]) || !is_array($value)) {
22860 $value = array($value);
22861 }
22862 } elseif (!empty($value)) {
22863 // Account for repeated nodes that must be an array: Foo => Baz, Foo => Baz, but only if the
22864 // value is set and not empty
22865 $value = array($value);
22866 }
22867 }
22868
22869 foreach ($value as &$item) {
22870 $this->recursiveProcess($param->getItems(), $item);
22871 }
22872 }
22873
22874 /**
22875 * Process an object
22876 *
22877 * @param Parameter $param API parameter being parsed
22878 * @param mixed $value Value to process
22879 */
22880 protected function processObject(Parameter $param, &$value)
22881 {
22882 // Ensure that the array is associative and not numerically indexed
22883 if (!isset($value[0]) && ($properties = $param->getProperties())) {
22884 $knownProperties = array();
22885 foreach ($properties as $property) {
22886 $name = $property->getName();
22887 $sentAs = $property->getWireName();
22888 $knownProperties[$name] = 1;
22889 if ($property->getData('xmlAttribute')) {
22890 $this->processXmlAttribute($property, $value);
22891 } elseif (isset($value[$sentAs])) {
22892 $this->recursiveProcess($property, $value[$sentAs]);
22893 if ($name != $sentAs) {
22894 $value[$name] = $value[$sentAs];
22895 unset($value[$sentAs]);
22896 }
22897 }
22898 }
22899
22900 // Remove any unknown and potentially unsafe properties
22901 if ($param->getAdditionalProperties() === false) {
22902 $value = array_intersect_key($value, $knownProperties);
22903 }
22904 }
22905 }
22906
22907 /**
22908 * Process an XML attribute property
22909 *
22910 * @param Parameter $property Property to process
22911 * @param array $value Value to process and update
22912 */
22913 protected function processXmlAttribute(Parameter $property, array &$value)
22914 {
22915 $sentAs = $property->getWireName();
22916 if (isset($value['@attributes'][$sentAs])) {
22917 $value[$property->getName()] = $value['@attributes'][$sentAs];
22918 unset($value['@attributes'][$sentAs]);
22919 if (empty($value['@attributes'])) {
22920 unset($value['@attributes']);
22921 }
22922 }
22923 }
22924 }
22925 <?php
22926
22927 namespace Guzzle\Service\Command\LocationVisitor\Response;
22928
22929 use Guzzle\Http\Message\Response;
22930 use Guzzle\Service\Description\Parameter;
22931 use Guzzle\Service\Command\CommandInterface;
22932
22933 /**
22934 * Location visitor used to add the status code of a response to a key in the result
22935 */
22936 class StatusCodeVisitor extends AbstractResponseVisitor
22937 {
22938 public function visit(
22939 CommandInterface $command,
22940 Response $response,
22941 Parameter $param,
22942 &$value,
22943 $context = null
22944 ) {
22945 $value[$param->getName()] = $response->getStatusCode();
22946 }
22947 }
22948 <?php
22949
22950 namespace Guzzle\Service\Command\LocationVisitor\Response;
22951
22952 use Guzzle\Service\Command\CommandInterface;
22953 use Guzzle\Http\Message\Response;
22954 use Guzzle\Service\Description\Parameter;
22955
22956 /**
22957 * {@inheritdoc}
22958 * @codeCoverageIgnore
22959 */
22960 abstract class AbstractResponseVisitor implements ResponseVisitorInterface
22961 {
22962 public function before(CommandInterface $command, array &$result) {}
22963
22964 public function after(CommandInterface $command) {}
22965
22966 public function visit(
22967 CommandInterface $command,
22968 Response $response,
22969 Parameter $param,
22970 &$value,
22971 $context = null
22972 ) {}
22973 }
22974 <?php
22975
22976 namespace Guzzle\Service\Command\LocationVisitor\Response;
22977
22978 use Guzzle\Service\Command\CommandInterface;
22979 use Guzzle\Http\Message\Response;
22980 use Guzzle\Service\Description\Parameter;
22981
22982 /**
22983 * Visitor used to add the body of a response to a particular key
22984 */
22985 class BodyVisitor extends AbstractResponseVisitor
22986 {
22987 public function visit(
22988 CommandInterface $command,
22989 Response $response,
22990 Parameter $param,
22991 &$value,
22992 $context = null
22993 ) {
22994 $value[$param->getName()] = $param->filter($response->getBody());
22995 }
22996 }
22997 <?php
22998
22999 namespace Guzzle\Service\Command\LocationVisitor\Response;
23000
23001 use Guzzle\Http\Message\Response;
23002 use Guzzle\Service\Description\Parameter;
23003 use Guzzle\Service\Command\CommandInterface;
23004
23005 /**
23006 * Location visitor used to add the reason phrase of a response to a key in the result
23007 */
23008 class ReasonPhraseVisitor extends AbstractResponseVisitor
23009 {
23010 public function visit(
23011 CommandInterface $command,
23012 Response $response,
23013 Parameter $param,
23014 &$value,
23015 $context = null
23016 ) {
23017 $value[$param->getName()] = $response->getReasonPhrase();
23018 }
23019 }
23020 <?php
23021
23022 namespace Guzzle\Service\Command\LocationVisitor\Response;
23023
23024 use Guzzle\Http\Message\Response;
23025 use Guzzle\Service\Description\Parameter;
23026 use Guzzle\Service\Command\CommandInterface;
23027
23028 /**
23029 * Location visitor used to parse values out of a response into an associative array
23030 */
23031 interface ResponseVisitorInterface
23032 {
23033 /**
23034 * Called before visiting all parameters. This can be used for seeding the result of a command with default
23035 * data (e.g. populating with JSON data in the response then adding to the parsed data).
23036 *
23037 * @param CommandInterface $command Command being visited
23038 * @param array $result Result value to update if needed (e.g. parsing XML or JSON)
23039 */
23040 public function before(CommandInterface $command, array &$result);
23041
23042 /**
23043 * Called after visiting all parameters
23044 *
23045 * @param CommandInterface $command Command being visited
23046 */
23047 public function after(CommandInterface $command);
23048
23049 /**
23050 * Called once for each parameter being visited that matches the location type
23051 *
23052 * @param CommandInterface $command Command being visited
23053 * @param Response $response Response being visited
23054 * @param Parameter $param Parameter being visited
23055 * @param mixed $value Result associative array value being updated by reference
23056 * @param mixed $context Parsing context
23057 */
23058 public function visit(
23059 CommandInterface $command,
23060 Response $response,
23061 Parameter $param,
23062 &$value,
23063 $context = null
23064 );
23065 }
23066 <?php
23067
23068 namespace Guzzle\Service\Command\LocationVisitor\Response;
23069
23070 use Guzzle\Http\Message\Response;
23071 use Guzzle\Service\Description\Parameter;
23072 use Guzzle\Service\Command\CommandInterface;
23073
23074 /**
23075 * Location visitor used to marshal JSON response data into a formatted array.
23076 *
23077 * Allows top level JSON parameters to be inserted into the result of a command. The top level attributes are grabbed
23078 * from the response's JSON data using the name value by default. Filters can be applied to parameters as they are
23079 * traversed. This allows data to be normalized before returning it to users (for example converting timestamps to
23080 * DateTime objects).
23081 */
23082 class JsonVisitor extends AbstractResponseVisitor
23083 {
23084 public function before(CommandInterface $command, array &$result)
23085 {
23086 // Ensure that the result of the command is always rooted with the parsed JSON data
23087 $result = $command->getResponse()->json();
23088 }
23089
23090 public function visit(
23091 CommandInterface $command,
23092 Response $response,
23093 Parameter $param,
23094 &$value,
23095 $context = null
23096 ) {
23097 $name = $param->getName();
23098 $key = $param->getWireName();
23099 if (isset($value[$key])) {
23100 $this->recursiveProcess($param, $value[$key]);
23101 if ($key != $name) {
23102 $value[$name] = $value[$key];
23103 unset($value[$key]);
23104 }
23105 }
23106 }
23107
23108 /**
23109 * Recursively process a parameter while applying filters
23110 *
23111 * @param Parameter $param API parameter being validated
23112 * @param mixed $value Value to validate and process. The value may change during this process.
23113 */
23114 protected function recursiveProcess(Parameter $param, &$value)
23115 {
23116 if ($value === null) {
23117 return;
23118 }
23119
23120 if (is_array($value)) {
23121 $type = $param->getType();
23122 if ($type == 'array') {
23123 foreach ($value as &$item) {
23124 $this->recursiveProcess($param->getItems(), $item);
23125 }
23126 } elseif ($type == 'object' && !isset($value[0])) {
23127 // On the above line, we ensure that the array is associative and not numerically indexed
23128 $knownProperties = array();
23129 if ($properties = $param->getProperties()) {
23130 foreach ($properties as $property) {
23131 $name = $property->getName();
23132 $key = $property->getWireName();
23133 $knownProperties[$name] = 1;
23134 if (isset($value[$key])) {
23135 $this->recursiveProcess($property, $value[$key]);
23136 if ($key != $name) {
23137 $value[$name] = $value[$key];
23138 unset($value[$key]);
23139 }
23140 }
23141 }
23142 }
23143
23144 // Remove any unknown and potentially unsafe properties
23145 if ($param->getAdditionalProperties() === false) {
23146 $value = array_intersect_key($value, $knownProperties);
23147 }
23148 }
23149 }
23150
23151 $value = $param->filter($value);
23152 }
23153 }
23154 <?php
23155
23156 namespace Guzzle\Service\Command\LocationVisitor\Response;
23157
23158 use Guzzle\Http\Message\Response;
23159 use Guzzle\Service\Description\Parameter;
23160 use Guzzle\Service\Command\CommandInterface;
23161
23162 /**
23163 * Location visitor used to add a particular header of a response to a key in the result
23164 */
23165 class HeaderVisitor extends AbstractResponseVisitor
23166 {
23167 public function visit(
23168 CommandInterface $command,
23169 Response $response,
23170 Parameter $param,
23171 &$value,
23172 $context = null
23173 ) {
23174 if ($param->getType() == 'object' && $param->getAdditionalProperties() instanceof Parameter) {
23175 $this->processPrefixedHeaders($response, $param, $value);
23176 } else {
23177 $value[$param->getName()] = $param->filter((string) $response->getHeader($param->getWireName()));
23178 }
23179 }
23180
23181 /**
23182 * Process a prefixed header array
23183 *
23184 * @param Response $response Response that contains the headers
23185 * @param Parameter $param Parameter object
23186 * @param array $value Value response array to modify
23187 */
23188 protected function processPrefixedHeaders(Response $response, Parameter $param, &$value)
23189 {
23190 // Grab prefixed headers that should be placed into an array with the prefix stripped
23191 if ($prefix = $param->getSentAs()) {
23192 $container = $param->getName();
23193 $len = strlen($prefix);
23194 // Find all matching headers and place them into the containing element
23195 foreach ($response->getHeaders()->toArray() as $key => $header) {
23196 if (stripos($key, $prefix) === 0) {
23197 // Account for multi-value headers
23198 $value[$container][substr($key, $len)] = count($header) == 1 ? end($header) : $header;
23199 }
23200 }
23201 }
23202 }
23203 }
23204 <?php
23205
23206 namespace Guzzle\Service\Command\LocationVisitor;
23207
23208 use Guzzle\Common\Exception\InvalidArgumentException;
23209 use Guzzle\Service\Command\LocationVisitor\Request\RequestVisitorInterface;
23210 use Guzzle\Service\Command\LocationVisitor\Response\ResponseVisitorInterface;
23211
23212 /**
23213 * Flyweight factory used to instantiate request and response visitors
23214 */
23215 class VisitorFlyweight
23216 {
23217 /** @var self Singleton instance of self */
23218 protected static $instance;
23219
23220 /** @var array Default array of mappings of location names to classes */
23221 protected static $defaultMappings = array(
23222 'request.body' => 'Guzzle\Service\Command\LocationVisitor\Request\BodyVisitor',
23223 'request.header' => 'Guzzle\Service\Command\LocationVisitor\Request\HeaderVisitor',
23224 'request.json' => 'Guzzle\Service\Command\LocationVisitor\Request\JsonVisitor',
23225 'request.postField' => 'Guzzle\Service\Command\LocationVisitor\Request\PostFieldVisitor',
23226 'request.postFile' => 'Guzzle\Service\Command\LocationVisitor\Request\PostFileVisitor',
23227 'request.query' => 'Guzzle\Service\Command\LocationVisitor\Request\QueryVisitor',
23228 'request.response_body' => 'Guzzle\Service\Command\LocationVisitor\Request\ResponseBodyVisitor',
23229 'request.responseBody' => 'Guzzle\Service\Command\LocationVisitor\Request\ResponseBodyVisitor',
23230 'request.xml' => 'Guzzle\Service\Command\LocationVisitor\Request\XmlVisitor',
23231 'response.body' => 'Guzzle\Service\Command\LocationVisitor\Response\BodyVisitor',
23232 'response.header' => 'Guzzle\Service\Command\LocationVisitor\Response\HeaderVisitor',
23233 'response.json' => 'Guzzle\Service\Command\LocationVisitor\Response\JsonVisitor',
23234 'response.reasonPhrase' => 'Guzzle\Service\Command\LocationVisitor\Response\ReasonPhraseVisitor',
23235 'response.statusCode' => 'Guzzle\Service\Command\LocationVisitor\Response\StatusCodeVisitor',
23236 'response.xml' => 'Guzzle\Service\Command\LocationVisitor\Response\XmlVisitor'
23237 );
23238
23239 /** @var array Array of mappings of location names to classes */
23240 protected $mappings;
23241
23242 /** @var array Cache of instantiated visitors */
23243 protected $cache = array();
23244
23245 /**
23246 * @return self
23247 * @codeCoverageIgnore
23248 */
23249 public static function getInstance()
23250 {
23251 if (!self::$instance) {
23252 self::$instance = new self();
23253 }
23254
23255 return self::$instance;
23256 }
23257
23258 /**
23259 * @param array $mappings Array mapping request.name and response.name to location visitor classes. Leave null to
23260 * use the default values.
23261 */
23262 public function __construct(array $mappings = null)
23263 {
23264 $this->mappings = $mappings === null ? self::$defaultMappings : $mappings;
23265 }
23266
23267 /**
23268 * Get an instance of a request visitor by location name
23269 *
23270 * @param string $visitor Visitor name
23271 *
23272 * @return RequestVisitorInterface
23273 */
23274 public function getRequestVisitor($visitor)
23275 {
23276 return $this->getKey('request.' . $visitor);
23277 }
23278
23279 /**
23280 * Get an instance of a response visitor by location name
23281 *
23282 * @param string $visitor Visitor name
23283 *
23284 * @return ResponseVisitorInterface
23285 */
23286 public function getResponseVisitor($visitor)
23287 {
23288 return $this->getKey('response.' . $visitor);
23289 }
23290
23291 /**
23292 * Add a response visitor to the factory by name
23293 *
23294 * @param string $name Name of the visitor
23295 * @param RequestVisitorInterface $visitor Visitor to add
23296 *
23297 * @return self
23298 */
23299 public function addRequestVisitor($name, RequestVisitorInterface $visitor)
23300 {
23301 $this->cache['request.' . $name] = $visitor;
23302
23303 return $this;
23304 }
23305
23306 /**
23307 * Add a response visitor to the factory by name
23308 *
23309 * @param string $name Name of the visitor
23310 * @param ResponseVisitorInterface $visitor Visitor to add
23311 *
23312 * @return self
23313 */
23314 public function addResponseVisitor($name, ResponseVisitorInterface $visitor)
23315 {
23316 $this->cache['response.' . $name] = $visitor;
23317
23318 return $this;
23319 }
23320
23321 /**
23322 * Get a visitor by key value name
23323 *
23324 * @param string $key Key name to retrieve
23325 *
23326 * @return mixed
23327 * @throws InvalidArgumentException
23328 */
23329 private function getKey($key)
23330 {
23331 if (!isset($this->cache[$key])) {
23332 if (!isset($this->mappings[$key])) {
23333 list($type, $name) = explode('.', $key);
23334 throw new InvalidArgumentException("No {$type} visitor has been mapped for {$name}");
23335 }
23336 $this->cache[$key] = new $this->mappings[$key];
23337 }
23338
23339 return $this->cache[$key];
23340 }
23341 }
23342 <?php
23343
23344 namespace Guzzle\Service\Command;
23345
23346 use Guzzle\Http\Message\Response;
23347 use Guzzle\Service\Command\LocationVisitor\VisitorFlyweight;
23348 use Guzzle\Service\Command\LocationVisitor\Response\ResponseVisitorInterface;
23349 use Guzzle\Service\Description\Parameter;
23350 use Guzzle\Service\Description\OperationInterface;
23351 use Guzzle\Service\Description\Operation;
23352 use Guzzle\Service\Exception\ResponseClassException;
23353 use Guzzle\Service\Resource\Model;
23354
23355 /**
23356 * Response parser that attempts to marshal responses into an associative array based on models in a service description
23357 */
23358 class OperationResponseParser extends DefaultResponseParser
23359 {
23360 /** @var VisitorFlyweight $factory Visitor factory */
23361 protected $factory;
23362
23363 /** @var self */
23364 protected static $instance;
23365
23366 /** @var bool */
23367 private $schemaInModels;
23368
23369 /**
23370 * @return self
23371 * @codeCoverageIgnore
23372 */
23373 public static function getInstance()
23374 {
23375 if (!static::$instance) {
23376 static::$instance = new static(VisitorFlyweight::getInstance());
23377 }
23378
23379 return static::$instance;
23380 }
23381
23382 /**
23383 * @param VisitorFlyweight $factory Factory to use when creating visitors
23384 * @param bool $schemaInModels Set to true to inject schemas into models
23385 */
23386 public function __construct(VisitorFlyweight $factory, $schemaInModels = false)
23387 {
23388 $this->factory = $factory;
23389 $this->schemaInModels = $schemaInModels;
23390 }
23391
23392 /**
23393 * Add a location visitor to the command
23394 *
23395 * @param string $location Location to associate with the visitor
23396 * @param ResponseVisitorInterface $visitor Visitor to attach
23397 *
23398 * @return self
23399 */
23400 public function addVisitor($location, ResponseVisitorInterface $visitor)
23401 {
23402 $this->factory->addResponseVisitor($location, $visitor);
23403
23404 return $this;
23405 }
23406
23407 protected function handleParsing(CommandInterface $command, Response $response, $contentType)
23408 {
23409 $operation = $command->getOperation();
23410 $type = $operation->getResponseType();
23411 $model = null;
23412
23413 if ($type == OperationInterface::TYPE_MODEL) {
23414 $model = $operation->getServiceDescription()->getModel($operation->getResponseClass());
23415 } elseif ($type == OperationInterface::TYPE_CLASS) {
23416 return $this->parseClass($command);
23417 }
23418
23419 if (!$model) {
23420 // Return basic processing if the responseType is not model or the model cannot be found
23421 return parent::handleParsing($command, $response, $contentType);
23422 } elseif ($command[AbstractCommand::RESPONSE_PROCESSING] != AbstractCommand::TYPE_MODEL) {
23423 // Returns a model with no visiting if the command response processing is not model
23424 return new Model(parent::handleParsing($command, $response, $contentType));
23425 } else {
23426 // Only inject the schema into the model if "schemaInModel" is true
23427 return new Model($this->visitResult($model, $command, $response), $this->schemaInModels ? $model : null);
23428 }
23429 }
23430
23431 /**
23432 * Parse a class object
23433 *
23434 * @param CommandInterface $command Command to parse into an object
23435 *
23436 * @return mixed
23437 * @throws ResponseClassException
23438 */
23439 protected function parseClass(CommandInterface $command)
23440 {
23441 // Emit the operation.parse_class event. If a listener injects a 'result' property, then that will be the result
23442 $event = new CreateResponseClassEvent(array('command' => $command));
23443 $command->getClient()->getEventDispatcher()->dispatch('command.parse_response', $event);
23444 if ($result = $event->getResult()) {
23445 return $result;
23446 }
23447
23448 $className = $command->getOperation()->getResponseClass();
23449 if (!method_exists($className, 'fromCommand')) {
23450 throw new ResponseClassException("{$className} must exist and implement a static fromCommand() method");
23451 }
23452
23453 return $className::fromCommand($command);
23454 }
23455
23456 /**
23457 * Perform transformations on the result array
23458 *
23459 * @param Parameter $model Model that defines the structure
23460 * @param CommandInterface $command Command that performed the operation
23461 * @param Response $response Response received
23462 *
23463 * @return array Returns the array of result data
23464 */
23465 protected function visitResult(Parameter $model, CommandInterface $command, Response $response)
23466 {
23467 $foundVisitors = $result = $knownProps = array();
23468 $props = $model->getProperties();
23469
23470 foreach ($props as $schema) {
23471 if ($location = $schema->getLocation()) {
23472 // Trigger the before method on the first found visitor of this type
23473 if (!isset($foundVisitors[$location])) {
23474 $foundVisitors[$location] = $this->factory->getResponseVisitor($location);
23475 $foundVisitors[$location]->before($command, $result);
23476 }
23477 }
23478 }
23479
23480 // Visit additional properties when it is an actual schema
23481 if (($additional = $model->getAdditionalProperties()) instanceof Parameter) {
23482 $this->visitAdditionalProperties($model, $command, $response, $additional, $result, $foundVisitors);
23483 }
23484
23485 // Apply the parameter value with the location visitor
23486 foreach ($props as $schema) {
23487 $knownProps[$schema->getName()] = 1;
23488 if ($location = $schema->getLocation()) {
23489 $foundVisitors[$location]->visit($command, $response, $schema, $result);
23490 }
23491 }
23492
23493 // Remove any unknown and potentially unsafe top-level properties
23494 if ($additional === false) {
23495 $result = array_intersect_key($result, $knownProps);
23496 }
23497
23498 // Call the after() method of each found visitor
23499 foreach ($foundVisitors as $visitor) {
23500 $visitor->after($command);
23501 }
23502
23503 return $result;
23504 }
23505
23506 protected function visitAdditionalProperties(
23507 Parameter $model,
23508 CommandInterface $command,
23509 Response $response,
23510 Parameter $additional,
23511 &$result,
23512 array &$foundVisitors
23513 ) {
23514 // Only visit when a location is specified
23515 if ($location = $additional->getLocation()) {
23516 if (!isset($foundVisitors[$location])) {
23517 $foundVisitors[$location] = $this->factory->getResponseVisitor($location);
23518 $foundVisitors[$location]->before($command, $result);
23519 }
23520 // Only traverse if an array was parsed from the before() visitors
23521 if (is_array($result)) {
23522 // Find each additional property
23523 foreach (array_keys($result) as $key) {
23524 // Check if the model actually knows this property. If so, then it is not additional
23525 if (!$model->getProperty($key)) {
23526 // Set the name to the key so that we can parse it with each visitor
23527 $additional->setName($key);
23528 $foundVisitors[$location]->visit($command, $response, $additional, $result);
23529 }
23530 }
23531 // Reset the additionalProperties name to null
23532 $additional->setName(null);
23533 }
23534 }
23535 }
23536 }
23537 <?php
23538
23539 namespace Guzzle\Service\Command;
23540
23541 use Guzzle\Common\Event;
23542
23543 /**
23544 * Event class emitted with the operation.parse_class event
23545 */
23546 class CreateResponseClassEvent extends Event
23547 {
23548 /**
23549 * Set the result of the object creation
23550 *
23551 * @param mixed $result Result value to set
23552 */
23553 public function setResult($result)
23554 {
23555 $this['result'] = $result;
23556 $this->stopPropagation();
23557 }
23558
23559 /**
23560 * Get the created object
23561 *
23562 * @return mixed
23563 */
23564 public function getResult()
23565 {
23566 return $this['result'];
23567 }
23568 }
23569 <?php
23570
23571 namespace Guzzle\Service;
23572
23573 use Guzzle\Common\Exception\InvalidArgumentException;
23574 use Guzzle\Common\Exception\RuntimeException;
23575
23576 /**
23577 * Abstract config loader
23578 */
23579 abstract class AbstractConfigLoader implements ConfigLoaderInterface
23580 {
23581 /** @var array Array of aliases for actual filenames */
23582 protected $aliases = array();
23583
23584 /** @var array Hash of previously loaded filenames */
23585 protected $loadedFiles = array();
23586
23587 /** @var array JSON error code mappings */
23588 protected static $jsonErrors = array(
23589 JSON_ERROR_NONE => 'JSON_ERROR_NONE - No errors',
23590 JSON_ERROR_DEPTH => 'JSON_ERROR_DEPTH - Maximum stack depth exceeded',
23591 JSON_ERROR_STATE_MISMATCH => 'JSON_ERROR_STATE_MISMATCH - Underflow or the modes mismatch',
23592 JSON_ERROR_CTRL_CHAR => 'JSON_ERROR_CTRL_CHAR - Unexpected control character found',
23593 JSON_ERROR_SYNTAX => 'JSON_ERROR_SYNTAX - Syntax error, malformed JSON',
23594 JSON_ERROR_UTF8 => 'JSON_ERROR_UTF8 - Malformed UTF-8 characters, possibly incorrectly encoded'
23595 );
23596
23597 public function load($config, array $options = array())
23598 {
23599 // Reset the array of loaded files because this is a new config
23600 $this->loadedFiles = array();
23601
23602 if (is_string($config)) {
23603 $config = $this->loadFile($config);
23604 } elseif (!is_array($config)) {
23605 throw new InvalidArgumentException('Unknown type passed to configuration loader: ' . gettype($config));
23606 } else {
23607 $this->mergeIncludes($config);
23608 }
23609
23610 return $this->build($config, $options);
23611 }
23612
23613 /**
23614 * Add an include alias to the loader
23615 *
23616 * @param string $filename Filename to alias (e.g. _foo)
23617 * @param string $alias Actual file to use (e.g. /path/to/foo.json)
23618 *
23619 * @return self
23620 */
23621 public function addAlias($filename, $alias)
23622 {
23623 $this->aliases[$filename] = $alias;
23624
23625 return $this;
23626 }
23627
23628 /**
23629 * Remove an alias from the loader
23630 *
23631 * @param string $alias Alias to remove
23632 *
23633 * @return self
23634 */
23635 public function removeAlias($alias)
23636 {
23637 unset($this->aliases[$alias]);
23638
23639 return $this;
23640 }
23641
23642 /**
23643 * Perform the parsing of a config file and create the end result
23644 *
23645 * @param array $config Configuration data
23646 * @param array $options Options to use when building
23647 *
23648 * @return mixed
23649 */
23650 protected abstract function build($config, array $options);
23651
23652 /**
23653 * Load a configuration file (can load JSON or PHP files that return an array when included)
23654 *
23655 * @param string $filename File to load
23656 *
23657 * @return array
23658 * @throws InvalidArgumentException
23659 * @throws RuntimeException when the JSON cannot be parsed
23660 */
23661 protected function loadFile($filename)
23662 {
23663 if (isset($this->aliases[$filename])) {
23664 $filename = $this->aliases[$filename];
23665 }
23666
23667 switch (pathinfo($filename, PATHINFO_EXTENSION)) {
23668 case 'js':
23669 case 'json':
23670 $level = error_reporting(0);
23671 $json = file_get_contents($filename);
23672 error_reporting($level);
23673
23674 if ($json === false) {
23675 $err = error_get_last();
23676 throw new InvalidArgumentException("Unable to open {$filename}: " . $err['message']);
23677 }
23678
23679 $config = json_decode($json, true);
23680 // Throw an exception if there was an error loading the file
23681 if ($error = json_last_error()) {
23682 $message = isset(self::$jsonErrors[$error]) ? self::$jsonErrors[$error] : 'Unknown error';
23683 throw new RuntimeException("Error loading JSON data from {$filename}: ({$error}) - {$message}");
23684 }
23685 break;
23686 case 'php':
23687 if (!is_readable($filename)) {
23688 throw new InvalidArgumentException("Unable to open {$filename} for reading");
23689 }
23690 $config = require $filename;
23691 if (!is_array($config)) {
23692 throw new InvalidArgumentException('PHP files must return an array of configuration data');
23693 }
23694 break;
23695 default:
23696 throw new InvalidArgumentException('Unknown file extension: ' . $filename);
23697 }
23698
23699 // Keep track of this file being loaded to prevent infinite recursion
23700 $this->loadedFiles[$filename] = true;
23701
23702 // Merge include files into the configuration array
23703 $this->mergeIncludes($config, dirname($filename));
23704
23705 return $config;
23706 }
23707
23708 /**
23709 * Merges in all include files
23710 *
23711 * @param array $config Config data that contains includes
23712 * @param string $basePath Base path to use when a relative path is encountered
23713 *
23714 * @return array Returns the merged and included data
23715 */
23716 protected function mergeIncludes(&$config, $basePath = null)
23717 {
23718 if (!empty($config['includes'])) {
23719 foreach ($config['includes'] as &$path) {
23720 // Account for relative paths
23721 if ($path[0] != DIRECTORY_SEPARATOR && !isset($this->aliases[$path]) && $basePath) {
23722 $path = "{$basePath}/{$path}";
23723 }
23724 // Don't load the same files more than once
23725 if (!isset($this->loadedFiles[$path])) {
23726 $this->loadedFiles[$path] = true;
23727 $config = $this->mergeData($this->loadFile($path), $config);
23728 }
23729 }
23730 }
23731 }
23732
23733 /**
23734 * Default implementation for merging two arrays of data (uses array_merge_recursive)
23735 *
23736 * @param array $a Original data
23737 * @param array $b Data to merge into the original and overwrite existing values
23738 *
23739 * @return array
23740 */
23741 protected function mergeData(array $a, array $b)
23742 {
23743 return array_merge_recursive($a, $b);
23744 }
23745 }
23746 <?php
23747
23748 namespace Guzzle\Batch;
23749
23750 /**
23751 * Abstract decorator used when decorating a BatchInterface
23752 */
23753 abstract class AbstractBatchDecorator implements BatchInterface
23754 {
23755 /** @var BatchInterface Decorated batch object */
23756 protected $decoratedBatch;
23757
23758 /**
23759 * @param BatchInterface $decoratedBatch BatchInterface that is being decorated
23760 */
23761 public function __construct(BatchInterface $decoratedBatch)
23762 {
23763 $this->decoratedBatch = $decoratedBatch;
23764 }
23765
23766 /**
23767 * Allow decorators to implement custom methods
23768 *
23769 * @param string $method Missing method name
23770 * @param array $args Method arguments
23771 *
23772 * @return mixed
23773 * @codeCoverageIgnore
23774 */
23775 public function __call($method, array $args)
23776 {
23777 return call_user_func_array(array($this->decoratedBatch, $method), $args);
23778 }
23779
23780 public function add($item)
23781 {
23782 $this->decoratedBatch->add($item);
23783
23784 return $this;
23785 }
23786
23787 public function flush()
23788 {
23789 return $this->decoratedBatch->flush();
23790 }
23791
23792 public function isEmpty()
23793 {
23794 return $this->decoratedBatch->isEmpty();
23795 }
23796
23797 /**
23798 * Trace the decorators associated with the batch
23799 *
23800 * @return array
23801 */
23802 public function getDecorators()
23803 {
23804 $found = array($this);
23805 if (method_exists($this->decoratedBatch, 'getDecorators')) {
23806 $found = array_merge($found, $this->decoratedBatch->getDecorators());
23807 }
23808
23809 return $found;
23810 }
23811 }
23812 <?php
23813
23814 namespace Guzzle\Batch;
23815
23816 /**
23817 * Interface for efficiently transferring items in a queue using batches
23818 */
23819 interface BatchInterface
23820 {
23821 /**
23822 * Add an item to the queue
23823 *
23824 * @param mixed $item Item to add
23825 *
23826 * @return self
23827 */
23828 public function add($item);
23829
23830 /**
23831 * Flush the batch and transfer the items
23832 *
23833 * @return array Returns an array flushed items
23834 */
23835 public function flush();
23836
23837 /**
23838 * Check if the batch is empty and has further items to transfer
23839 *
23840 * @return bool
23841 */
23842 public function isEmpty();
23843 }
23844 <?php
23845
23846 namespace Guzzle\Batch;
23847
23848 use Guzzle\Batch\Exception\BatchTransferException;
23849
23850 /**
23851 * BatchInterface decorator used to buffer exceptions encountered during a transfer. The exceptions can then later be
23852 * processed after a batch flush has completed.
23853 */
23854 class ExceptionBufferingBatch extends AbstractBatchDecorator
23855 {
23856 /** @var array Array of BatchTransferException exceptions */
23857 protected $exceptions = array();
23858
23859 public function flush()
23860 {
23861 $items = array();
23862
23863 while (!$this->decoratedBatch->isEmpty()) {
23864 try {
23865 $transferredItems = $this->decoratedBatch->flush();
23866 } catch (BatchTransferException $e) {
23867 $this->exceptions[] = $e;
23868 $transferredItems = $e->getTransferredItems();
23869 }
23870 $items = array_merge($items, $transferredItems);
23871 }
23872
23873 return $items;
23874 }
23875
23876 /**
23877 * Get the buffered exceptions
23878 *
23879 * @return array Array of BatchTransferException objects
23880 */
23881 public function getExceptions()
23882 {
23883 return $this->exceptions;
23884 }
23885
23886 /**
23887 * Clear the buffered exceptions
23888 */
23889 public function clearExceptions()
23890 {
23891 $this->exceptions = array();
23892 }
23893 }
23894 <?php
23895
23896 namespace Guzzle\Batch;
23897
23898 /**
23899 * Divides batches into smaller batches under a certain size
23900 */
23901 class BatchSizeDivisor implements BatchDivisorInterface
23902 {
23903 /** @var int Size of each batch */
23904 protected $size;
23905
23906 /** @param int $size Size of each batch */
23907 public function __construct($size)
23908 {
23909 $this->size = $size;
23910 }
23911
23912 /**
23913 * Set the size of each batch
23914 *
23915 * @param int $size Size of each batch
23916 *
23917 * @return BatchSizeDivisor
23918 */
23919 public function setSize($size)
23920 {
23921 $this->size = $size;
23922
23923 return $this;
23924 }
23925
23926 /**
23927 * Get the size of each batch
23928 *
23929 * @return int
23930 */
23931 public function getSize()
23932 {
23933 return $this->size;
23934 }
23935
23936 public function createBatches(\SplQueue $queue)
23937 {
23938 return array_chunk(iterator_to_array($queue, false), $this->size);
23939 }
23940 }
23941 <?php
23942
23943 namespace Guzzle\Batch;
23944
23945 use Guzzle\Batch\BatchTransferInterface;
23946 use Guzzle\Batch\BatchDivisorInterface;
23947 use Guzzle\Common\Exception\InvalidArgumentException;
23948 use Guzzle\Service\Command\CommandInterface;
23949 use Guzzle\Service\Exception\InconsistentClientTransferException;
23950
23951 /**
23952 * Efficiently transfers multiple commands in parallel per client
23953 * This class is to be used with {@see Guzzle\Batch\BatchInterface}
23954 */
23955 class BatchCommandTransfer implements BatchTransferInterface, BatchDivisorInterface
23956 {
23957 /** @var int Size of each command batch */
23958 protected $batchSize;
23959
23960 /**
23961 * @param int $batchSize Size of each batch
23962 */
23963 public function __construct($batchSize = 50)
23964 {
23965 $this->batchSize = $batchSize;
23966 }
23967
23968 /**
23969 * Creates batches by grouping commands by their associated client
23970 * {@inheritdoc}
23971 */
23972 public function createBatches(\SplQueue $queue)
23973 {
23974 $groups = new \SplObjectStorage();
23975 foreach ($queue as $item) {
23976 if (!$item instanceof CommandInterface) {
23977 throw new InvalidArgumentException('All items must implement Guzzle\Service\Command\CommandInterface');
23978 }
23979 $client = $item->getClient();
23980 if (!$groups->contains($client)) {
23981 $groups->attach($client, new \ArrayObject(array($item)));
23982 } else {
23983 $groups[$client]->append($item);
23984 }
23985 }
23986
23987 $batches = array();
23988 foreach ($groups as $batch) {
23989 $batches = array_merge($batches, array_chunk($groups[$batch]->getArrayCopy(), $this->batchSize));
23990 }
23991
23992 return $batches;
23993 }
23994
23995 public function transfer(array $batch)
23996 {
23997 if (empty($batch)) {
23998 return;
23999 }
24000
24001 // Get the client of the first found command
24002 $client = reset($batch)->getClient();
24003
24004 // Keep a list of all commands with invalid clients
24005 $invalid = array_filter($batch, function ($command) use ($client) {
24006 return $command->getClient() !== $client;
24007 });
24008
24009 if (!empty($invalid)) {
24010 throw new InconsistentClientTransferException($invalid);
24011 }
24012
24013 $client->execute($batch);
24014 }
24015 }
24016 <?php
24017
24018 namespace Guzzle\Batch;
24019
24020 use Guzzle\Common\Exception\InvalidArgumentException;
24021
24022 /**
24023 * BatchInterface decorator used to call a method each time flush is called
24024 */
24025 class NotifyingBatch extends AbstractBatchDecorator
24026 {
24027 /** @var mixed Callable to call */
24028 protected $callable;
24029
24030 /**
24031 * @param BatchInterface $decoratedBatch Batch object to decorate
24032 * @param mixed $callable Callable to call
24033 *
24034 * @throws InvalidArgumentException
24035 */
24036 public function __construct(BatchInterface $decoratedBatch, $callable)
24037 {
24038 if (!is_callable($callable)) {
24039 throw new InvalidArgumentException('The passed argument is not callable');
24040 }
24041
24042 $this->callable = $callable;
24043 parent::__construct($decoratedBatch);
24044 }
24045
24046 public function flush()
24047 {
24048 $items = $this->decoratedBatch->flush();
24049 call_user_func($this->callable, $items);
24050
24051 return $items;
24052 }
24053 }
24054 <?php
24055
24056 namespace Guzzle\Batch;
24057
24058 /**
24059 * BatchInterface decorator used to add automatic flushing of the queue when the size of the queue reaches a threshold.
24060 */
24061 class FlushingBatch extends AbstractBatchDecorator
24062 {
24063 /** @var int The threshold for which to automatically flush */
24064 protected $threshold;
24065
24066 /** @var int Current number of items known to be in the queue */
24067 protected $currentTotal = 0;
24068
24069 /**
24070 * @param BatchInterface $decoratedBatch BatchInterface that is being decorated
24071 * @param int $threshold Flush when the number in queue matches the threshold
24072 */
24073 public function __construct(BatchInterface $decoratedBatch, $threshold)
24074 {
24075 $this->threshold = $threshold;
24076 parent::__construct($decoratedBatch);
24077 }
24078
24079 /**
24080 * Set the auto-flush threshold
24081 *
24082 * @param int $threshold The auto-flush threshold
24083 *
24084 * @return FlushingBatch
24085 */
24086 public function setThreshold($threshold)
24087 {
24088 $this->threshold = $threshold;
24089
24090 return $this;
24091 }
24092
24093 /**
24094 * Get the auto-flush threshold
24095 *
24096 * @return int
24097 */
24098 public function getThreshold()
24099 {
24100 return $this->threshold;
24101 }
24102
24103 public function add($item)
24104 {
24105 $this->decoratedBatch->add($item);
24106 if (++$this->currentTotal >= $this->threshold) {
24107 $this->currentTotal = 0;
24108 $this->decoratedBatch->flush();
24109 }
24110
24111 return $this;
24112 }
24113 }
24114 <?php
24115
24116 namespace Guzzle\Batch;
24117
24118 use Guzzle\Batch\Exception\BatchTransferException;
24119
24120 /**
24121 * Default batch implementation used to convert queued items into smaller chunks of batches using a
24122 * {@see BatchDivisorIterface} and transfers each batch using a {@see BatchTransferInterface}.
24123 *
24124 * Any exception encountered during a flush operation will throw a {@see BatchTransferException} object containing the
24125 * batch that failed. After an exception is encountered, you can flush the batch again to attempt to finish transferring
24126 * any previously created batches or queued items.
24127 */
24128 class Batch implements BatchInterface
24129 {
24130 /** @var \SplQueue Queue of items in the queue */
24131 protected $queue;
24132
24133 /** @var array Divided batches to be transferred */
24134 protected $dividedBatches;
24135
24136 /** @var BatchTransferInterface */
24137 protected $transferStrategy;
24138
24139 /** @var BatchDivisorInterface */
24140 protected $divisionStrategy;
24141
24142 /**
24143 * @param BatchTransferInterface $transferStrategy Strategy used to transfer items
24144 * @param BatchDivisorInterface $divisionStrategy Divisor used to create batches
24145 */
24146 public function __construct(BatchTransferInterface $transferStrategy, BatchDivisorInterface $divisionStrategy)
24147 {
24148 $this->transferStrategy = $transferStrategy;
24149 $this->divisionStrategy = $divisionStrategy;
24150 $this->queue = new \SplQueue();
24151 $this->queue->setIteratorMode(\SplQueue::IT_MODE_DELETE);
24152 $this->dividedBatches = array();
24153 }
24154
24155 public function add($item)
24156 {
24157 $this->queue->enqueue($item);
24158
24159 return $this;
24160 }
24161
24162 public function flush()
24163 {
24164 $this->createBatches();
24165
24166 $items = array();
24167 foreach ($this->dividedBatches as $batchIndex => $dividedBatch) {
24168 while ($dividedBatch->valid()) {
24169 $batch = $dividedBatch->current();
24170 $dividedBatch->next();
24171 try {
24172 $this->transferStrategy->transfer($batch);
24173 $items = array_merge($items, $batch);
24174 } catch (\Exception $e) {
24175 throw new BatchTransferException($batch, $items, $e, $this->transferStrategy, $this->divisionStrategy);
24176 }
24177 }
24178 // Keep the divided batch down to a minimum in case of a later exception
24179 unset($this->dividedBatches[$batchIndex]);
24180 }
24181
24182 return $items;
24183 }
24184
24185 public function isEmpty()
24186 {
24187 return count($this->queue) == 0 && count($this->dividedBatches) == 0;
24188 }
24189
24190 /**
24191 * Create batches for any queued items
24192 */
24193 protected function createBatches()
24194 {
24195 if (count($this->queue)) {
24196 if ($batches = $this->divisionStrategy->createBatches($this->queue)) {
24197 // Convert arrays into iterators
24198 if (is_array($batches)) {
24199 $batches = new \ArrayIterator($batches);
24200 }
24201 $this->dividedBatches[] = $batches;
24202 }
24203 }
24204 }
24205 }
24206 <?php
24207
24208 namespace Guzzle\Batch;
24209
24210 /**
24211 * Interface used for dividing a queue of items into an array of batches
24212 */
24213 interface BatchDivisorInterface
24214 {
24215 /**
24216 * Divide a queue of items into an array batches
24217 *
24218 * @param \SplQueue $queue Queue of items to divide into batches. Items are removed as they are iterated.
24219 *
24220 * @return array|\Traversable Returns an array or Traversable object that contains arrays of items to transfer
24221 */
24222 public function createBatches(\SplQueue $queue);
24223 }
24224 <?php
24225
24226 namespace Guzzle\Batch\Exception;
24227
24228 use Guzzle\Common\Exception\GuzzleException;
24229 use Guzzle\Batch\BatchTransferInterface as TransferStrategy;
24230 use Guzzle\Batch\BatchDivisorInterface as DivisorStrategy;
24231
24232 /**
24233 * Exception thrown during a batch transfer
24234 */
24235 class BatchTransferException extends \Exception implements GuzzleException
24236 {
24237 /** @var array The batch being sent when the exception occurred */
24238 protected $batch;
24239
24240 /** @var TransferStrategy The transfer strategy in use when the exception occurred */
24241 protected $transferStrategy;
24242
24243 /** @var DivisorStrategy The divisor strategy in use when the exception occurred */
24244 protected $divisorStrategy;
24245
24246 /** @var array Items transferred at the point in which the exception was encountered */
24247 protected $transferredItems;
24248
24249 /**
24250 * @param array $batch The batch being sent when the exception occurred
24251 * @param array $transferredItems Items transferred at the point in which the exception was encountered
24252 * @param \Exception $exception Exception encountered
24253 * @param TransferStrategy $transferStrategy The transfer strategy in use when the exception occurred
24254 * @param DivisorStrategy $divisorStrategy The divisor strategy in use when the exception occurred
24255 */
24256 public function __construct(
24257 array $batch,
24258 array $transferredItems,
24259 \Exception $exception,
24260 TransferStrategy $transferStrategy = null,
24261 DivisorStrategy $divisorStrategy = null
24262 ) {
24263 $this->batch = $batch;
24264 $this->transferredItems = $transferredItems;
24265 $this->transferStrategy = $transferStrategy;
24266 $this->divisorStrategy = $divisorStrategy;
24267 parent::__construct(
24268 'Exception encountered while transferring batch: ' . $exception->getMessage(),
24269 $exception->getCode(),
24270 $exception
24271 );
24272 }
24273
24274 /**
24275 * Get the batch that we being sent when the exception occurred
24276 *
24277 * @return array
24278 */
24279 public function getBatch()
24280 {
24281 return $this->batch;
24282 }
24283
24284 /**
24285 * Get the items transferred at the point in which the exception was encountered
24286 *
24287 * @return array
24288 */
24289 public function getTransferredItems()
24290 {
24291 return $this->transferredItems;
24292 }
24293
24294 /**
24295 * Get the transfer strategy
24296 *
24297 * @return TransferStrategy
24298 */
24299 public function getTransferStrategy()
24300 {
24301 return $this->transferStrategy;
24302 }
24303
24304 /**
24305 * Get the divisor strategy
24306 *
24307 * @return DivisorStrategy
24308 */
24309 public function getDivisorStrategy()
24310 {
24311 return $this->divisorStrategy;
24312 }
24313 }
24314 <?php
24315
24316 namespace Guzzle\Batch;
24317
24318 use Guzzle\Common\Exception\InvalidArgumentException;
24319
24320 /**
24321 * Divides batches using a callable
24322 */
24323 class BatchClosureDivisor implements BatchDivisorInterface
24324 {
24325 /** @var callable Method used to divide the batches */
24326 protected $callable;
24327
24328 /** @var mixed $context Context passed to the callable */
24329 protected $context;
24330
24331 /**
24332 * @param callable $callable Method used to divide the batches. The method must accept an \SplQueue and return an
24333 * array of arrays containing the divided items.
24334 * @param mixed $context Optional context to pass to the batch divisor
24335 *
24336 * @throws InvalidArgumentException if the callable is not callable
24337 */
24338 public function __construct($callable, $context = null)
24339 {
24340 if (!is_callable($callable)) {
24341 throw new InvalidArgumentException('Must pass a callable');
24342 }
24343
24344 $this->callable = $callable;
24345 $this->context = $context;
24346 }
24347
24348 public function createBatches(\SplQueue $queue)
24349 {
24350 return call_user_func($this->callable, $queue, $this->context);
24351 }
24352 }
24353 <?php
24354
24355 namespace Guzzle\Batch;
24356
24357 use Guzzle\Common\Exception\InvalidArgumentException;
24358 use Guzzle\Common\Exception\RuntimeException;
24359
24360 /**
24361 * Builder used to create custom batch objects
24362 */
24363 class BatchBuilder
24364 {
24365 /** @var bool Whether or not the batch should automatically flush*/
24366 protected $autoFlush = false;
24367
24368 /** @var bool Whether or not to maintain a batch history */
24369 protected $history = false;
24370
24371 /** @var bool Whether or not to buffer exceptions encountered in transfer */
24372 protected $exceptionBuffering = false;
24373
24374 /** @var mixed Callable to invoke each time a flush completes */
24375 protected $afterFlush;
24376
24377 /** @var BatchTransferInterface Object used to transfer items in the queue */
24378 protected $transferStrategy;
24379
24380 /** @var BatchDivisorInterface Object used to divide the queue into batches */
24381 protected $divisorStrategy;
24382
24383 /** @var array of Mapped transfer strategies by handle name */
24384 protected static $mapping = array(
24385 'request' => 'Guzzle\Batch\BatchRequestTransfer',
24386 'command' => 'Guzzle\Batch\BatchCommandTransfer'
24387 );
24388
24389 /**
24390 * Create a new instance of the BatchBuilder
24391 *
24392 * @return BatchBuilder
24393 */
24394 public static function factory()
24395 {
24396 return new self();
24397 }
24398
24399 /**
24400 * Automatically flush the batch when the size of the queue reaches a certain threshold. Adds {@see FlushingBatch}.
24401 *
24402 * @param $threshold Number of items to allow in the queue before a flush
24403 *
24404 * @return BatchBuilder
24405 */
24406 public function autoFlushAt($threshold)
24407 {
24408 $this->autoFlush = $threshold;
24409
24410 return $this;
24411 }
24412
24413 /**
24414 * Maintain a history of all items that have been transferred using the batch. Adds {@see HistoryBatch}.
24415 *
24416 * @return BatchBuilder
24417 */
24418 public function keepHistory()
24419 {
24420 $this->history = true;
24421
24422 return $this;
24423 }
24424
24425 /**
24426 * Buffer exceptions thrown during transfer so that you can transfer as much as possible, and after a transfer
24427 * completes, inspect each exception that was thrown. Enables the {@see ExceptionBufferingBatch} decorator.
24428 *
24429 * @return BatchBuilder
24430 */
24431 public function bufferExceptions()
24432 {
24433 $this->exceptionBuffering = true;
24434
24435 return $this;
24436 }
24437
24438 /**
24439 * Notify a callable each time a batch flush completes. Enables the {@see NotifyingBatch} decorator.
24440 *
24441 * @param mixed $callable Callable function to notify
24442 *
24443 * @return BatchBuilder
24444 * @throws InvalidArgumentException if the argument is not callable
24445 */
24446 public function notify($callable)
24447 {
24448 $this->afterFlush = $callable;
24449
24450 return $this;
24451 }
24452
24453 /**
24454 * Configures the batch to transfer batches of requests. Associates a {@see \Guzzle\Http\BatchRequestTransfer}
24455 * object as both the transfer and divisor strategy.
24456 *
24457 * @param int $batchSize Batch size for each batch of requests
24458 *
24459 * @return BatchBuilder
24460 */
24461 public function transferRequests($batchSize = 50)
24462 {
24463 $className = self::$mapping['request'];
24464 $this->transferStrategy = new $className($batchSize);
24465 $this->divisorStrategy = $this->transferStrategy;
24466
24467 return $this;
24468 }
24469
24470 /**
24471 * Configures the batch to transfer batches commands. Associates as
24472 * {@see \Guzzle\Service\Command\BatchCommandTransfer} as both the transfer and divisor strategy.
24473 *
24474 * @param int $batchSize Batch size for each batch of commands
24475 *
24476 * @return BatchBuilder
24477 */
24478 public function transferCommands($batchSize = 50)
24479 {
24480 $className = self::$mapping['command'];
24481 $this->transferStrategy = new $className($batchSize);
24482 $this->divisorStrategy = $this->transferStrategy;
24483
24484 return $this;
24485 }
24486
24487 /**
24488 * Specify the strategy used to divide the queue into an array of batches
24489 *
24490 * @param BatchDivisorInterface $divisorStrategy Strategy used to divide a batch queue into batches
24491 *
24492 * @return BatchBuilder
24493 */
24494 public function createBatchesWith(BatchDivisorInterface $divisorStrategy)
24495 {
24496 $this->divisorStrategy = $divisorStrategy;
24497
24498 return $this;
24499 }
24500
24501 /**
24502 * Specify the strategy used to transport the items when flush is called
24503 *
24504 * @param BatchTransferInterface $transferStrategy How items are transferred
24505 *
24506 * @return BatchBuilder
24507 */
24508 public function transferWith(BatchTransferInterface $transferStrategy)
24509 {
24510 $this->transferStrategy = $transferStrategy;
24511
24512 return $this;
24513 }
24514
24515 /**
24516 * Create and return the instantiated batch
24517 *
24518 * @return BatchInterface
24519 * @throws RuntimeException if no transfer strategy has been specified
24520 */
24521 public function build()
24522 {
24523 if (!$this->transferStrategy) {
24524 throw new RuntimeException('No transfer strategy has been specified');
24525 }
24526
24527 if (!$this->divisorStrategy) {
24528 throw new RuntimeException('No divisor strategy has been specified');
24529 }
24530
24531 $batch = new Batch($this->transferStrategy, $this->divisorStrategy);
24532
24533 if ($this->exceptionBuffering) {
24534 $batch = new ExceptionBufferingBatch($batch);
24535 }
24536
24537 if ($this->afterFlush) {
24538 $batch = new NotifyingBatch($batch, $this->afterFlush);
24539 }
24540
24541 if ($this->autoFlush) {
24542 $batch = new FlushingBatch($batch, $this->autoFlush);
24543 }
24544
24545 if ($this->history) {
24546 $batch = new HistoryBatch($batch);
24547 }
24548
24549 return $batch;
24550 }
24551 }
24552 <?php
24553
24554 namespace Guzzle\Batch;
24555
24556 /**
24557 * Interface used for transferring batches of items
24558 */
24559 interface BatchTransferInterface
24560 {
24561 /**
24562 * Transfer an array of items
24563 *
24564 * @param array $batch Array of items to transfer
24565 */
24566 public function transfer(array $batch);
24567 }
24568 {
24569 "name": "guzzle/batch",
24570 "description": "Guzzle batch component for batching requests, commands, or custom transfers",
24571 "homepage": "http://guzzlephp.org/",
24572 "keywords": ["batch", "HTTP", "REST", "guzzle"],
24573 "license": "MIT",
24574 "authors": [
24575 {
24576 "name": "Michael Dowling",
24577 "email": "mtdowling@gmail.com",
24578 "homepage": "https://github.com/mtdowling"
24579 }
24580 ],
24581 "require": {
24582 "php": ">=5.3.2",
24583 "guzzle/common": "self.version"
24584 },
24585 "autoload": {
24586 "psr-0": { "Guzzle\\Batch": "" }
24587 },
24588 "suggest": {
24589 "guzzle/http": "self.version",
24590 "guzzle/service": "self.version"
24591 },
24592 "target-dir": "Guzzle/Batch",
24593 "extra": {
24594 "branch-alias": {
24595 "dev-master": "3.7-dev"
24596 }
24597 }
24598 }
24599 <?php
24600
24601 namespace Guzzle\Batch;
24602
24603 /**
24604 * BatchInterface decorator used to keep a history of items that were added to the batch. You must clear the history
24605 * manually to remove items from the history.
24606 */
24607 class HistoryBatch extends AbstractBatchDecorator
24608 {
24609 /** @var array Items in the history */
24610 protected $history = array();
24611
24612 public function add($item)
24613 {
24614 $this->history[] = $item;
24615 $this->decoratedBatch->add($item);
24616
24617 return $this;
24618 }
24619
24620 /**
24621 * Get the batch history
24622 *
24623 * @return array
24624 */
24625 public function getHistory()
24626 {
24627 return $this->history;
24628 }
24629
24630 /**
24631 * Clear the batch history
24632 */
24633 public function clearHistory()
24634 {
24635 $this->history = array();
24636 }
24637 }
24638 <?php
24639
24640 namespace Guzzle\Batch;
24641
24642 use Guzzle\Common\Exception\InvalidArgumentException;
24643
24644 /**
24645 * Batch transfer strategy where transfer logic can be defined via a Closure.
24646 * This class is to be used with {@see Guzzle\Batch\BatchInterface}
24647 */
24648 class BatchClosureTransfer implements BatchTransferInterface
24649 {
24650 /** @var callable A closure that performs the transfer */
24651 protected $callable;
24652
24653 /** @var mixed $context Context passed to the callable */
24654 protected $context;
24655
24656 /**
24657 * @param mixed $callable Callable that performs the transfer. This function should accept two arguments:
24658 * (array $batch, mixed $context).
24659 * @param mixed $context Optional context to pass to the batch divisor
24660 *
24661 * @throws InvalidArgumentException
24662 */
24663 public function __construct($callable, $context = null)
24664 {
24665 if (!is_callable($callable)) {
24666 throw new InvalidArgumentException('Argument must be callable');
24667 }
24668
24669 $this->callable = $callable;
24670 $this->context = $context;
24671 }
24672
24673 public function transfer(array $batch)
24674 {
24675 return empty($batch) ? null : call_user_func($this->callable, $batch, $this->context);
24676 }
24677 }
24678 <?php
24679
24680 namespace Guzzle\Batch;
24681
24682 use Guzzle\Batch\BatchTransferInterface;
24683 use Guzzle\Batch\BatchDivisorInterface;
24684 use Guzzle\Common\Exception\InvalidArgumentException;
24685 use Guzzle\Http\Message\RequestInterface;
24686
24687 /**
24688 * Batch transfer strategy used to efficiently transfer a batch of requests.
24689 * This class is to be used with {@see Guzzle\Batch\BatchInterface}
24690 */
24691 class BatchRequestTransfer implements BatchTransferInterface, BatchDivisorInterface
24692 {
24693 /** @var int Size of each command batch */
24694 protected $batchSize;
24695
24696 /**
24697 * Constructor used to specify how large each batch should be
24698 *
24699 * @param int $batchSize Size of each batch
24700 */
24701 public function __construct($batchSize = 50)
24702 {
24703 $this->batchSize = $batchSize;
24704 }
24705
24706 /**
24707 * Creates batches of requests by grouping requests by their associated curl multi object.
24708 * {@inheritdoc}
24709 */
24710 public function createBatches(\SplQueue $queue)
24711 {
24712 // Create batches by client objects
24713 $groups = new \SplObjectStorage();
24714 foreach ($queue as $item) {
24715 if (!$item instanceof RequestInterface) {
24716 throw new InvalidArgumentException('All items must implement Guzzle\Http\Message\RequestInterface');
24717 }
24718 $client = $item->getClient();
24719 if (!$groups->contains($client)) {
24720 $groups->attach($client, array($item));
24721 } else {
24722 $current = $groups[$client];
24723 $current[] = $item;
24724 $groups[$client] = $current;
24725 }
24726 }
24727
24728 $batches = array();
24729 foreach ($groups as $batch) {
24730 $batches = array_merge($batches, array_chunk($groups[$batch], $this->batchSize));
24731 }
24732
24733 return $batches;
24734 }
24735
24736 public function transfer(array $batch)
24737 {
24738 if ($batch) {
24739 reset($batch)->getClient()->send($batch);
24740 }
24741 }
24742 }
24743 <?php
24744
24745 namespace Guzzle\Parser\Url;
24746
24747 /**
24748 * URL parser interface
24749 */
24750 interface UrlParserInterface
24751 {
24752 /**
24753 * Parse a URL using special handling for a subset of UTF-8 characters in the query string if needed.
24754 *
24755 * @param string $url URL to parse
24756 *
24757 * @return array Returns an array identical to what is returned from parse_url(). When an array key is missing from
24758 * this array, you must fill it in with NULL to avoid warnings in calling code.
24759 */
24760 public function parseUrl($url);
24761 }
24762 <?php
24763
24764 namespace Guzzle\Parser\Url;
24765
24766 use Guzzle\Common\Version;
24767
24768 /**
24769 * Parses URLs into parts using PHP's built-in parse_url() function
24770 * @deprecated Just use parse_url. UTF-8 characters should be percent encoded anyways.
24771 * @codeCoverageIgnore
24772 */
24773 class UrlParser implements UrlParserInterface
24774 {
24775 /** @var bool Whether or not to work with UTF-8 strings */
24776 protected $utf8 = false;
24777
24778 /**
24779 * Set whether or not to attempt to handle UTF-8 strings (still WIP)
24780 *
24781 * @param bool $utf8 Set to TRUE to handle UTF string
24782 */
24783 public function setUtf8Support($utf8)
24784 {
24785 $this->utf8 = $utf8;
24786 }
24787
24788 public function parseUrl($url)
24789 {
24790 Version::warn(__CLASS__ . ' is deprecated. Just use parse_url()');
24791
24792 static $defaults = array('scheme' => null, 'host' => null, 'path' => null, 'port' => null, 'query' => null,
24793 'user' => null, 'pass' => null, 'fragment' => null);
24794
24795 $parts = parse_url($url);
24796
24797 // Need to handle query parsing specially for UTF-8 requirements
24798 if ($this->utf8 && isset($parts['query'])) {
24799 $queryPos = strpos($url, '?');
24800 if (isset($parts['fragment'])) {
24801 $parts['query'] = substr($url, $queryPos + 1, strpos($url, '#') - $queryPos - 1);
24802 } else {
24803 $parts['query'] = substr($url, $queryPos + 1);
24804 }
24805 }
24806
24807 return $parts + $defaults;
24808 }
24809 }
24810 <?php
24811
24812 namespace Guzzle\Parser;
24813
24814 /**
24815 * Registry of parsers used by the application
24816 */
24817 class ParserRegistry
24818 {
24819 /** @var ParserRegistry Singleton instance */
24820 protected static $instance;
24821
24822 /** @var array Array of parser instances */
24823 protected $instances = array();
24824
24825 /** @var array Mapping of parser name to default class */
24826 protected $mapping = array(
24827 'message' => 'Guzzle\\Parser\\Message\\MessageParser',
24828 'cookie' => 'Guzzle\\Parser\\Cookie\\CookieParser',
24829 'url' => 'Guzzle\\Parser\\Url\\UrlParser',
24830 'uri_template' => 'Guzzle\\Parser\\UriTemplate\\UriTemplate',
24831 );
24832
24833 /**
24834 * @return self
24835 * @codeCoverageIgnore
24836 */
24837 public static function getInstance()
24838 {
24839 if (!self::$instance) {
24840 self::$instance = new static;
24841 }
24842
24843 return self::$instance;
24844 }
24845
24846 public function __construct()
24847 {
24848 // Use the PECL URI template parser if available
24849 if (extension_loaded('uri_template')) {
24850 $this->mapping['uri_template'] = 'Guzzle\\Parser\\UriTemplate\\PeclUriTemplate';
24851 }
24852 }
24853
24854 /**
24855 * Get a parser by name from an instance
24856 *
24857 * @param string $name Name of the parser to retrieve
24858 *
24859 * @return mixed|null
24860 */
24861 public function getParser($name)
24862 {
24863 if (!isset($this->instances[$name])) {
24864 if (!isset($this->mapping[$name])) {
24865 return null;
24866 }
24867 $class = $this->mapping[$name];
24868 $this->instances[$name] = new $class();
24869 }
24870
24871 return $this->instances[$name];
24872 }
24873
24874 /**
24875 * Register a custom parser by name with the register
24876 *
24877 * @param string $name Name or handle of the parser to register
24878 * @param mixed $parser Instantiated parser to register
24879 */
24880 public function registerParser($name, $parser)
24881 {
24882 $this->instances[$name] = $parser;
24883 }
24884 }
24885 <?php
24886
24887 namespace Guzzle\Parser\UriTemplate;
24888
24889 /**
24890 * Expands URI templates using an array of variables
24891 *
24892 * @link http://tools.ietf.org/html/draft-gregorio-uritemplate-08
24893 */
24894 class UriTemplate implements UriTemplateInterface
24895 {
24896 /** @var string URI template */
24897 private $template;
24898
24899 /** @var array Variables to use in the template expansion */
24900 private $variables;
24901
24902 /** @var string Regex used to parse expressions */
24903 private static $regex = '/\{([^\}]+)\}/';
24904
24905 /** @var array Hash for quick operator lookups */
24906 private static $operatorHash = array(
24907 '+' => true, '#' => true, '.' => true, '/' => true, ';' => true, '?' => true, '&' => true
24908 );
24909
24910 /** @var array Delimiters */
24911 private static $delims = array(
24912 ':', '/', '?', '#', '[', ']', '@', '!', '$', '&', '\'', '(', ')', '*', '+', ',', ';', '='
24913 );
24914
24915 /** @var array Percent encoded delimiters */
24916 private static $delimsPct = array(
24917 '%3A', '%2F', '%3F', '%23', '%5B', '%5D', '%40', '%21', '%24', '%26', '%27', '%28', '%29', '%2A', '%2B', '%2C',
24918 '%3B', '%3D'
24919 );
24920
24921 public function expand($template, array $variables)
24922 {
24923 // Check to ensure that the preg_* function is needed
24924 if (false === strpos($template, '{')) {
24925 return $template;
24926 }
24927
24928 $this->template = $template;
24929 $this->variables = $variables;
24930
24931 return preg_replace_callback(self::$regex, array($this, 'expandMatch'), $this->template);
24932 }
24933
24934 /**
24935 * Parse an expression into parts
24936 *
24937 * @param string $expression Expression to parse
24938 *
24939 * @return array Returns an associative array of parts
24940 */
24941 private function parseExpression($expression)
24942 {
24943 // Check for URI operators
24944 $operator = '';
24945
24946 if (isset(self::$operatorHash[$expression[0]])) {
24947 $operator = $expression[0];
24948 $expression = substr($expression, 1);
24949 }
24950
24951 $values = explode(',', $expression);
24952 foreach ($values as &$value) {
24953 $value = trim($value);
24954 $varspec = array();
24955 $substrPos = strpos($value, ':');
24956 if ($substrPos) {
24957 $varspec['value'] = substr($value, 0, $substrPos);
24958 $varspec['modifier'] = ':';
24959 $varspec['position'] = (int) substr($value, $substrPos + 1);
24960 } elseif (substr($value, -1) == '*') {
24961 $varspec['modifier'] = '*';
24962 $varspec['value'] = substr($value, 0, -1);
24963 } else {
24964 $varspec['value'] = (string) $value;
24965 $varspec['modifier'] = '';
24966 }
24967 $value = $varspec;
24968 }
24969
24970 return array(
24971 'operator' => $operator,
24972 'values' => $values
24973 );
24974 }
24975
24976 /**
24977 * Process an expansion
24978 *
24979 * @param array $matches Matches met in the preg_replace_callback
24980 *
24981 * @return string Returns the replacement string
24982 */
24983 private function expandMatch(array $matches)
24984 {
24985 static $rfc1738to3986 = array(
24986 '+' => '%20',
24987 '%7e' => '~'
24988 );
24989
24990 $parsed = self::parseExpression($matches[1]);
24991 $replacements = array();
24992
24993 $prefix = $parsed['operator'];
24994 $joiner = $parsed['operator'];
24995 $useQueryString = false;
24996 if ($parsed['operator'] == '?') {
24997 $joiner = '&';
24998 $useQueryString = true;
24999 } elseif ($parsed['operator'] == '&') {
25000 $useQueryString = true;
25001 } elseif ($parsed['operator'] == '#') {
25002 $joiner = ',';
25003 } elseif ($parsed['operator'] == ';') {
25004 $useQueryString = true;
25005 } elseif ($parsed['operator'] == '' || $parsed['operator'] == '+') {
25006 $joiner = ',';
25007 $prefix = '';
25008 }
25009
25010 foreach ($parsed['values'] as $value) {
25011
25012 if (!array_key_exists($value['value'], $this->variables) || $this->variables[$value['value']] === null) {
25013 continue;
25014 }
25015
25016 $variable = $this->variables[$value['value']];
25017 $actuallyUseQueryString = $useQueryString;
25018 $expanded = '';
25019
25020 if (is_array($variable)) {
25021
25022 $isAssoc = $this->isAssoc($variable);
25023 $kvp = array();
25024 foreach ($variable as $key => $var) {
25025
25026 if ($isAssoc) {
25027 $key = rawurlencode($key);
25028 $isNestedArray = is_array($var);
25029 } else {
25030 $isNestedArray = false;
25031 }
25032
25033 if (!$isNestedArray) {
25034 $var = rawurlencode($var);
25035 if ($parsed['operator'] == '+' || $parsed['operator'] == '#') {
25036 $var = $this->decodeReserved($var);
25037 }
25038 }
25039
25040 if ($value['modifier'] == '*') {
25041 if ($isAssoc) {
25042 if ($isNestedArray) {
25043 // Nested arrays must allow for deeply nested structures
25044 $var = strtr(http_build_query(array($key => $var)), $rfc1738to3986);
25045 } else {
25046 $var = $key . '=' . $var;
25047 }
25048 } elseif ($key > 0 && $actuallyUseQueryString) {
25049 $var = $value['value'] . '=' . $var;
25050 }
25051 }
25052
25053 $kvp[$key] = $var;
25054 }
25055
25056 if (empty($variable)) {
25057 $actuallyUseQueryString = false;
25058 } elseif ($value['modifier'] == '*') {
25059 $expanded = implode($joiner, $kvp);
25060 if ($isAssoc) {
25061 // Don't prepend the value name when using the explode modifier with an associative array
25062 $actuallyUseQueryString = false;
25063 }
25064 } else {
25065 if ($isAssoc) {
25066 // When an associative array is encountered and the explode modifier is not set, then the
25067 // result must be a comma separated list of keys followed by their respective values.
25068 foreach ($kvp as $k => &$v) {
25069 $v = $k . ',' . $v;
25070 }
25071 }
25072 $expanded = implode(',', $kvp);
25073 }
25074
25075 } else {
25076 if ($value['modifier'] == ':') {
25077 $variable = substr($variable, 0, $value['position']);
25078 }
25079 $expanded = rawurlencode($variable);
25080 if ($parsed['operator'] == '+' || $parsed['operator'] == '#') {
25081 $expanded = $this->decodeReserved($expanded);
25082 }
25083 }
25084
25085 if ($actuallyUseQueryString) {
25086 if (!$expanded && $joiner != '&') {
25087 $expanded = $value['value'];
25088 } else {
25089 $expanded = $value['value'] . '=' . $expanded;
25090 }
25091 }
25092
25093 $replacements[] = $expanded;
25094 }
25095
25096 $ret = implode($joiner, $replacements);
25097 if ($ret && $prefix) {
25098 return $prefix . $ret;
25099 }
25100
25101 return $ret;
25102 }
25103
25104 /**
25105 * Determines if an array is associative
25106 *
25107 * @param array $array Array to check
25108 *
25109 * @return bool
25110 */
25111 private function isAssoc(array $array)
25112 {
25113 return (bool) count(array_filter(array_keys($array), 'is_string'));
25114 }
25115
25116 /**
25117 * Removes percent encoding on reserved characters (used with + and # modifiers)
25118 *
25119 * @param string $string String to fix
25120 *
25121 * @return string
25122 */
25123 private function decodeReserved($string)
25124 {
25125 return str_replace(self::$delimsPct, self::$delims, $string);
25126 }
25127 }
25128 <?php
25129
25130 namespace Guzzle\Parser\UriTemplate;
25131
25132 use Guzzle\Common\Exception\RuntimeException;
25133
25134 /**
25135 * Expands URI templates using the uri_template pecl extension (pecl install uri_template-beta)
25136 *
25137 * @link http://pecl.php.net/package/uri_template
25138 * @link https://github.com/ioseb/uri-template
25139 */
25140 class PeclUriTemplate implements UriTemplateInterface
25141 {
25142 public function __construct()
25143 {
25144 if (!extension_loaded('uri_template')) {
25145 throw new RuntimeException('uri_template PECL extension must be installed to use PeclUriTemplate');
25146 }
25147 }
25148
25149 public function expand($template, array $variables)
25150 {
25151 return uri_template($template, $variables);
25152 }
25153 }
25154 <?php
25155
25156 namespace Guzzle\Parser\UriTemplate;
25157
25158 /**
25159 * Expands URI templates using an array of variables
25160 *
25161 * @link http://tools.ietf.org/html/rfc6570
25162 */
25163 interface UriTemplateInterface
25164 {
25165 /**
25166 * Expand the URI template using the supplied variables
25167 *
25168 * @param string $template URI Template to expand
25169 * @param array $variables Variables to use with the expansion
25170 *
25171 * @return string Returns the expanded template
25172 */
25173 public function expand($template, array $variables);
25174 }
25175 <?php
25176
25177 namespace Guzzle\Parser\Cookie;
25178
25179 /**
25180 * Cookie parser interface
25181 */
25182 interface CookieParserInterface
25183 {
25184 /**
25185 * Parse a cookie string as set in a Set-Cookie HTTP header and return an associative array of data.
25186 *
25187 * @param string $cookie Cookie header value to parse
25188 * @param string $host Host of an associated request
25189 * @param string $path Path of an associated request
25190 * @param bool $decode Set to TRUE to urldecode cookie values
25191 *
25192 * @return array|bool Returns FALSE on failure or returns an array of arrays, with each of the sub arrays including:
25193 * - domain (string) - Domain of the cookie
25194 * - path (string) - Path of the cookie
25195 * - cookies (array) - Associative array of cookie names and values
25196 * - max_age (int) - Lifetime of the cookie in seconds
25197 * - version (int) - Version of the cookie specification. RFC 2965 is 1
25198 * - secure (bool) - Whether or not this is a secure cookie
25199 * - discard (bool) - Whether or not this is a discardable cookie
25200 * - custom (string) - Custom cookie data array
25201 * - comment (string) - How the cookie is intended to be used
25202 * - comment_url (str)- URL that contains info on how it will be used
25203 * - port (array|str) - Array of ports or null
25204 * - http_only (bool) - HTTP only cookie
25205 */
25206 public function parseCookie($cookie, $host = null, $path = null, $decode = false);
25207 }
25208 <?php
25209
25210 namespace Guzzle\Parser\Cookie;
25211
25212 /**
25213 * Default Guzzle implementation of a Cookie parser
25214 */
25215 class CookieParser implements CookieParserInterface
25216 {
25217 /** @var array Cookie part names to snake_case array values */
25218 protected static $cookieParts = array(
25219 'domain' => 'Domain',
25220 'path' => 'Path',
25221 'max_age' => 'Max-Age',
25222 'expires' => 'Expires',
25223 'version' => 'Version',
25224 'secure' => 'Secure',
25225 'port' => 'Port',
25226 'discard' => 'Discard',
25227 'comment' => 'Comment',
25228 'comment_url' => 'Comment-Url',
25229 'http_only' => 'HttpOnly'
25230 );
25231
25232 public function parseCookie($cookie, $host = null, $path = null, $decode = false)
25233 {
25234 // Explode the cookie string using a series of semicolons
25235 $pieces = array_filter(array_map('trim', explode(';', $cookie)));
25236
25237 // The name of the cookie (first kvp) must include an equal sign.
25238 if (empty($pieces) || !strpos($pieces[0], '=')) {
25239 return false;
25240 }
25241
25242 // Create the default return array
25243 $data = array_merge(array_fill_keys(array_keys(self::$cookieParts), null), array(
25244 'cookies' => array(),
25245 'data' => array(),
25246 'path' => $path ?: '/',
25247 'http_only' => false,
25248 'discard' => false,
25249 'domain' => $host
25250 ));
25251 $foundNonCookies = 0;
25252
25253 // Add the cookie pieces into the parsed data array
25254 foreach ($pieces as $part) {
25255
25256 $cookieParts = explode('=', $part, 2);
25257 $key = trim($cookieParts[0]);
25258
25259 if (count($cookieParts) == 1) {
25260 // Can be a single value (e.g. secure, httpOnly)
25261 $value = true;
25262 } else {
25263 // Be sure to strip wrapping quotes
25264 $value = trim($cookieParts[1], " \n\r\t\0\x0B\"");
25265 if ($decode) {
25266 $value = urldecode($value);
25267 }
25268 }
25269
25270 // Only check for non-cookies when cookies have been found
25271 if (!empty($data['cookies'])) {
25272 foreach (self::$cookieParts as $mapValue => $search) {
25273 if (!strcasecmp($search, $key)) {
25274 $data[$mapValue] = $mapValue == 'port' ? array_map('trim', explode(',', $value)) : $value;
25275 $foundNonCookies++;
25276 continue 2;
25277 }
25278 }
25279 }
25280
25281 // If cookies have not yet been retrieved, or this value was not found in the pieces array, treat it as a
25282 // cookie. IF non-cookies have been parsed, then this isn't a cookie, it's cookie data. Cookies then data.
25283 $data[$foundNonCookies ? 'data' : 'cookies'][$key] = $value;
25284 }
25285
25286 // Calculate the expires date
25287 if (!$data['expires'] && $data['max_age']) {
25288 $data['expires'] = time() + (int) $data['max_age'];
25289 }
25290
25291 return $data;
25292 }
25293 }
25294 <?php
25295
25296 namespace Guzzle\Parser\Message;
25297
25298 /**
25299 * HTTP message parser interface used to parse HTTP messages into an array
25300 */
25301 interface MessageParserInterface
25302 {
25303 /**
25304 * Parse an HTTP request message into an associative array of parts.
25305 *
25306 * @param string $message HTTP request to parse
25307 *
25308 * @return array|bool Returns false if the message is invalid
25309 */
25310 public function parseRequest($message);
25311
25312 /**
25313 * Parse an HTTP response message into an associative array of parts.
25314 *
25315 * @param string $message HTTP response to parse
25316 *
25317 * @return array|bool Returns false if the message is invalid
25318 */
25319 public function parseResponse($message);
25320 }
25321 <?php
25322
25323 namespace Guzzle\Parser\Message;
25324
25325 /**
25326 * Pecl HTTP message parser
25327 */
25328 class PeclHttpMessageParser extends AbstractMessageParser
25329 {
25330 public function parseRequest($message)
25331 {
25332 if (!$message) {
25333 return false;
25334 }
25335
25336 $parts = http_parse_message($message);
25337
25338 $parsed = array(
25339 'method' => $parts->requestMethod,
25340 'protocol' => 'HTTP',
25341 'version' => number_format($parts->httpVersion, 1),
25342 'headers' => $parts->headers,
25343 'body' => $parts->body
25344 );
25345
25346 $parsed['request_url'] = $this->getUrlPartsFromMessage($parts->requestUrl, $parsed);
25347
25348 return $parsed;
25349 }
25350
25351 public function parseResponse($message)
25352 {
25353 if (!$message) {
25354 return false;
25355 }
25356
25357 $parts = http_parse_message($message);
25358
25359 return array(
25360 'protocol' => 'HTTP',
25361 'version' => number_format($parts->httpVersion, 1),
25362 'code' => $parts->responseCode,
25363 'reason_phrase' => $parts->responseStatus,
25364 'headers' => $parts->headers,
25365 'body' => $parts->body
25366 );
25367 }
25368 }
25369 <?php
25370
25371 namespace Guzzle\Parser\Message;
25372
25373 /**
25374 * Default request and response parser used by Guzzle. Optimized for speed.
25375 */
25376 class MessageParser extends AbstractMessageParser
25377 {
25378 public function parseRequest($message)
25379 {
25380 if (!$message) {
25381 return false;
25382 }
25383
25384 $parts = $this->parseMessage($message);
25385
25386 // Parse the protocol and protocol version
25387 if (isset($parts['start_line'][2])) {
25388 $startParts = explode('/', $parts['start_line'][2]);
25389 $protocol = strtoupper($startParts[0]);
25390 $version = isset($startParts[1]) ? $startParts[1] : '1.1';
25391 } else {
25392 $protocol = 'HTTP';
25393 $version = '1.1';
25394 }
25395
25396 $parsed = array(
25397 'method' => strtoupper($parts['start_line'][0]),
25398 'protocol' => $protocol,
25399 'version' => $version,
25400 'headers' => $parts['headers'],
25401 'body' => $parts['body']
25402 );
25403
25404 $parsed['request_url'] = $this->getUrlPartsFromMessage($parts['start_line'][1], $parsed);
25405
25406 return $parsed;
25407 }
25408
25409 public function parseResponse($message)
25410 {
25411 if (!$message) {
25412 return false;
25413 }
25414
25415 $parts = $this->parseMessage($message);
25416 list($protocol, $version) = explode('/', trim($parts['start_line'][0]));
25417
25418 return array(
25419 'protocol' => $protocol,
25420 'version' => $version,
25421 'code' => $parts['start_line'][1],
25422 'reason_phrase' => isset($parts['start_line'][2]) ? $parts['start_line'][2] : '',
25423 'headers' => $parts['headers'],
25424 'body' => $parts['body']
25425 );
25426 }
25427
25428 /**
25429 * Parse a message into parts
25430 *
25431 * @param string $message Message to parse
25432 *
25433 * @return array
25434 */
25435 protected function parseMessage($message)
25436 {
25437 $startLine = null;
25438 $headers = array();
25439 $body = '';
25440
25441 // Iterate over each line in the message, accounting for line endings
25442 $lines = preg_split('/(\\r?\\n)/', $message, -1, PREG_SPLIT_DELIM_CAPTURE);
25443 for ($i = 0, $totalLines = count($lines); $i < $totalLines; $i += 2) {
25444
25445 $line = $lines[$i];
25446
25447 // If two line breaks were encountered, then this is the end of body
25448 if (empty($line)) {
25449 if ($i < $totalLines - 1) {
25450 $body = implode('', array_slice($lines, $i + 2));
25451 }
25452 break;
25453 }
25454
25455 // Parse message headers
25456 if (!$startLine) {
25457 $startLine = explode(' ', $line, 3);
25458 } elseif (strpos($line, ':')) {
25459 $parts = explode(':', $line, 2);
25460 $key = trim($parts[0]);
25461 $value = isset($parts[1]) ? trim($parts[1]) : '';
25462 if (!isset($headers[$key])) {
25463 $headers[$key] = $value;
25464 } elseif (!is_array($headers[$key])) {
25465 $headers[$key] = array($headers[$key], $value);
25466 } else {
25467 $headers[$key][] = $value;
25468 }
25469 }
25470 }
25471
25472 return array(
25473 'start_line' => $startLine,
25474 'headers' => $headers,
25475 'body' => $body
25476 );
25477 }
25478 }
25479 <?php
25480
25481 namespace Guzzle\Parser\Message;
25482
25483 /**
25484 * Implements shared message parsing functionality
25485 */
25486 abstract class AbstractMessageParser implements MessageParserInterface
25487 {
25488 /**
25489 * Create URL parts from HTTP message parts
25490 *
25491 * @param string $requestUrl Associated URL
25492 * @param array $parts HTTP message parts
25493 *
25494 * @return array
25495 */
25496 protected function getUrlPartsFromMessage($requestUrl, array $parts)
25497 {
25498 // Parse the URL information from the message
25499 $urlParts = array(
25500 'path' => $requestUrl,
25501 'scheme' => 'http'
25502 );
25503
25504 // Check for the Host header
25505 if (isset($parts['headers']['Host'])) {
25506 $urlParts['host'] = $parts['headers']['Host'];
25507 } elseif (isset($parts['headers']['host'])) {
25508 $urlParts['host'] = $parts['headers']['host'];
25509 } else {
25510 $urlParts['host'] = null;
25511 }
25512
25513 if (false === strpos($urlParts['host'], ':')) {
25514 $urlParts['port'] = '';
25515 } else {
25516 $hostParts = explode(':', $urlParts['host']);
25517 $urlParts['host'] = trim($hostParts[0]);
25518 $urlParts['port'] = (int) trim($hostParts[1]);
25519 if ($urlParts['port'] == 443) {
25520 $urlParts['scheme'] = 'https';
25521 }
25522 }
25523
25524 // Check if a query is present
25525 $path = $urlParts['path'];
25526 $qpos = strpos($path, '?');
25527 if ($qpos) {
25528 $urlParts['query'] = substr($path, $qpos + 1);
25529 $urlParts['path'] = substr($path, 0, $qpos);
25530 } else {
25531 $urlParts['query'] = '';
25532 }
25533
25534 return $urlParts;
25535 }
25536 }
25537 {
25538 "name": "guzzle/parser",
25539 "homepage": "http://guzzlephp.org/",
25540 "description": "Interchangeable parsers used by Guzzle",
25541 "keywords": ["HTTP", "message", "cookie", "URL", "URI Template"],
25542 "license": "MIT",
25543 "require": {
25544 "php": ">=5.3.2"
25545 },
25546 "autoload": {
25547 "psr-0": { "Guzzle\\Parser": "" }
25548 },
25549 "target-dir": "Guzzle/Parser",
25550 "extra": {
25551 "branch-alias": {
25552 "dev-master": "3.7-dev"
25553 }
25554 }
25555 }
25556 <?php
25557
25558 namespace Guzzle\Http\Curl;
25559
25560 use Guzzle\Common\Exception\InvalidArgumentException;
25561 use Guzzle\Common\Exception\RuntimeException;
25562 use Guzzle\Common\Collection;
25563 use Guzzle\Http\Message\EntityEnclosingRequest;
25564 use Guzzle\Http\Message\RequestInterface;
25565 use Guzzle\Parser\ParserRegistry;
25566 use Guzzle\Http\Url;
25567
25568 /**
25569 * Immutable wrapper for a cURL handle
25570 */
25571 class CurlHandle
25572 {
25573 const BODY_AS_STRING = 'body_as_string';
25574 const PROGRESS = 'progress';
25575 const DEBUG = 'debug';
25576
25577 /** @var Collection Curl options */
25578 protected $options;
25579
25580 /** @var resource Curl resource handle */
25581 protected $handle;
25582
25583 /** @var int CURLE_* error */
25584 protected $errorNo = CURLE_OK;
25585
25586 /**
25587 * Factory method to create a new curl handle based on an HTTP request.
25588 *
25589 * There are some helpful options you can set to enable specific behavior:
25590 * - debug: Set to true to enable cURL debug functionality to track the actual headers sent over the wire.
25591 * - progress: Set to true to enable progress function callbacks.
25592 *
25593 * @param RequestInterface $request Request
25594 *
25595 * @return CurlHandle
25596 * @throws RuntimeException
25597 */
25598 public static function factory(RequestInterface $request)
25599 {
25600 $requestCurlOptions = $request->getCurlOptions();
25601 $mediator = new RequestMediator($request, $requestCurlOptions->get('emit_io'));
25602 $tempContentLength = null;
25603 $method = $request->getMethod();
25604 $bodyAsString = $requestCurlOptions->get(self::BODY_AS_STRING);
25605
25606 // Array of default cURL options.
25607 $curlOptions = array(
25608 CURLOPT_URL => $request->getUrl(),
25609 CURLOPT_CONNECTTIMEOUT => 150,
25610 CURLOPT_RETURNTRANSFER => false,
25611 CURLOPT_HEADER => false,
25612 CURLOPT_PORT => $request->getPort(),
25613 CURLOPT_HTTPHEADER => array(),
25614 CURLOPT_WRITEFUNCTION => array($mediator, 'writeResponseBody'),
25615 CURLOPT_HEADERFUNCTION => array($mediator, 'receiveResponseHeader'),
25616 CURLOPT_HTTP_VERSION => $request->getProtocolVersion() === '1.0'
25617 ? CURL_HTTP_VERSION_1_0 : CURL_HTTP_VERSION_1_1,
25618 // Verifies the authenticity of the peer's certificate
25619 CURLOPT_SSL_VERIFYPEER => 1,
25620 // Certificate must indicate that the server is the server to which you meant to connect
25621 CURLOPT_SSL_VERIFYHOST => 2
25622 );
25623
25624 if (defined('CURLOPT_PROTOCOLS')) {
25625 // Allow only HTTP and HTTPS protocols
25626 $curlOptions[CURLOPT_PROTOCOLS] = CURLPROTO_HTTP | CURLPROTO_HTTPS;
25627 }
25628
25629 // Add CURLOPT_ENCODING if Accept-Encoding header is provided
25630 if ($acceptEncodingHeader = $request->getHeader('Accept-Encoding')) {
25631 $curlOptions[CURLOPT_ENCODING] = (string) $acceptEncodingHeader;
25632 // Let cURL set the Accept-Encoding header, prevents duplicate values
25633 $request->removeHeader('Accept-Encoding');
25634 }
25635
25636 // Enable curl debug information if the 'debug' param was set
25637 if ($requestCurlOptions->get('debug')) {
25638 $curlOptions[CURLOPT_STDERR] = fopen('php://temp', 'r+');
25639 // @codeCoverageIgnoreStart
25640 if (false === $curlOptions[CURLOPT_STDERR]) {
25641 throw new RuntimeException('Unable to create a stream for CURLOPT_STDERR');
25642 }
25643 // @codeCoverageIgnoreEnd
25644 $curlOptions[CURLOPT_VERBOSE] = true;
25645 }
25646
25647 // Specify settings according to the HTTP method
25648 if ($method == 'GET') {
25649 $curlOptions[CURLOPT_HTTPGET] = true;
25650 } elseif ($method == 'HEAD') {
25651 $curlOptions[CURLOPT_NOBODY] = true;
25652 // HEAD requests do not use a write function
25653 unset($curlOptions[CURLOPT_WRITEFUNCTION]);
25654 } elseif (!($request instanceof EntityEnclosingRequest)) {
25655 $curlOptions[CURLOPT_CUSTOMREQUEST] = $method;
25656 } else {
25657
25658 $curlOptions[CURLOPT_CUSTOMREQUEST] = $method;
25659
25660 // Handle sending raw bodies in a request
25661 if ($request->getBody()) {
25662 // You can send the body as a string using curl's CURLOPT_POSTFIELDS
25663 if ($bodyAsString) {
25664 $curlOptions[CURLOPT_POSTFIELDS] = (string) $request->getBody();
25665 // Allow curl to add the Content-Length for us to account for the times when
25666 // POST redirects are followed by GET requests
25667 if ($tempContentLength = $request->getHeader('Content-Length')) {
25668 $tempContentLength = (int) (string) $tempContentLength;
25669 }
25670 // Remove the curl generated Content-Type header if none was set manually
25671 if (!$request->hasHeader('Content-Type')) {
25672 $curlOptions[CURLOPT_HTTPHEADER][] = 'Content-Type:';
25673 }
25674 } else {
25675 $curlOptions[CURLOPT_UPLOAD] = true;
25676 // Let cURL handle setting the Content-Length header
25677 if ($tempContentLength = $request->getHeader('Content-Length')) {
25678 $tempContentLength = (int) (string) $tempContentLength;
25679 $curlOptions[CURLOPT_INFILESIZE] = $tempContentLength;
25680 }
25681 // Add a callback for curl to read data to send with the request only if a body was specified
25682 $curlOptions[CURLOPT_READFUNCTION] = array($mediator, 'readRequestBody');
25683 // Attempt to seek to the start of the stream
25684 $request->getBody()->seek(0);
25685 }
25686
25687 } else {
25688
25689 // Special handling for POST specific fields and files
25690 $postFields = false;
25691 if (count($request->getPostFiles())) {
25692 $postFields = $request->getPostFields()->useUrlEncoding(false)->urlEncode();
25693 foreach ($request->getPostFiles() as $key => $data) {
25694 $prefixKeys = count($data) > 1;
25695 foreach ($data as $index => $file) {
25696 // Allow multiple files in the same key
25697 $fieldKey = $prefixKeys ? "{$key}[{$index}]" : $key;
25698 $postFields[$fieldKey] = $file->getCurlValue();
25699 }
25700 }
25701 } elseif (count($request->getPostFields())) {
25702 $postFields = (string) $request->getPostFields()->useUrlEncoding(true);
25703 }
25704
25705 if ($postFields !== false) {
25706 if ($method == 'POST') {
25707 unset($curlOptions[CURLOPT_CUSTOMREQUEST]);
25708 $curlOptions[CURLOPT_POST] = true;
25709 }
25710 $curlOptions[CURLOPT_POSTFIELDS] = $postFields;
25711 $request->removeHeader('Content-Length');
25712 }
25713 }
25714
25715 // If the Expect header is not present, prevent curl from adding it
25716 if (!$request->hasHeader('Expect')) {
25717 $curlOptions[CURLOPT_HTTPHEADER][] = 'Expect:';
25718 }
25719 }
25720
25721 // If a Content-Length header was specified but we want to allow curl to set one for us
25722 if (null !== $tempContentLength) {
25723 $request->removeHeader('Content-Length');
25724 }
25725
25726 // Set custom cURL options
25727 foreach ($requestCurlOptions->toArray() as $key => $value) {
25728 if (is_numeric($key)) {
25729 $curlOptions[$key] = $value;
25730 }
25731 }
25732
25733 // Do not set an Accept header by default
25734 if (!isset($curlOptions[CURLOPT_ENCODING])) {
25735 $curlOptions[CURLOPT_HTTPHEADER][] = 'Accept:';
25736 }
25737
25738 // Add any custom headers to the request. Empty headers will cause curl to not send the header at all.
25739 foreach ($request->getHeaderLines() as $line) {
25740 $curlOptions[CURLOPT_HTTPHEADER][] = $line;
25741 }
25742
25743 // Add the content-length header back if it was temporarily removed
25744 if ($tempContentLength) {
25745 $request->setHeader('Content-Length', $tempContentLength);
25746 }
25747
25748 // Apply the options to a new cURL handle.
25749 $handle = curl_init();
25750
25751 // Enable the progress function if the 'progress' param was set
25752 if ($requestCurlOptions->get('progress')) {
25753 // Wrap the function in a function that provides the curl handle to the mediator's progress function
25754 // Using this rather than injecting the handle into the mediator prevents a circular reference
25755 $curlOptions[CURLOPT_PROGRESSFUNCTION] = function () use ($mediator, $handle) {
25756 $args = func_get_args();
25757 $args[] = $handle;
25758 call_user_func_array(array($mediator, 'progress'), $args);
25759 };
25760 $curlOptions[CURLOPT_NOPROGRESS] = false;
25761 }
25762
25763 curl_setopt_array($handle, $curlOptions);
25764
25765 return new static($handle, $curlOptions);
25766 }
25767
25768 /**
25769 * Construct a new CurlHandle object that wraps a cURL handle
25770 *
25771 * @param resource $handle Configured cURL handle resource
25772 * @param Collection|array $options Curl options to use with the handle
25773 *
25774 * @throws InvalidArgumentException
25775 */
25776 public function __construct($handle, $options)
25777 {
25778 if (!is_resource($handle)) {
25779 throw new InvalidArgumentException('Invalid handle provided');
25780 }
25781 if (is_array($options)) {
25782 $this->options = new Collection($options);
25783 } elseif ($options instanceof Collection) {
25784 $this->options = $options;
25785 } else {
25786 throw new InvalidArgumentException('Expected array or Collection');
25787 }
25788 $this->handle = $handle;
25789 }
25790
25791 /**
25792 * Destructor
25793 */
25794 public function __destruct()
25795 {
25796 $this->close();
25797 }
25798
25799 /**
25800 * Close the curl handle
25801 */
25802 public function close()
25803 {
25804 if (is_resource($this->handle)) {
25805 curl_close($this->handle);
25806 }
25807 $this->handle = null;
25808 }
25809
25810 /**
25811 * Check if the handle is available and still OK
25812 *
25813 * @return bool
25814 */
25815 public function isAvailable()
25816 {
25817 return is_resource($this->handle);
25818 }
25819
25820 /**
25821 * Get the last error that occurred on the cURL handle
25822 *
25823 * @return string
25824 */
25825 public function getError()
25826 {
25827 return $this->isAvailable() ? curl_error($this->handle) : '';
25828 }
25829
25830 /**
25831 * Get the last error number that occurred on the cURL handle
25832 *
25833 * @return int
25834 */
25835 public function getErrorNo()
25836 {
25837 if ($this->errorNo) {
25838 return $this->errorNo;
25839 }
25840
25841 return $this->isAvailable() ? curl_errno($this->handle) : CURLE_OK;
25842 }
25843
25844 /**
25845 * Set the curl error number
25846 *
25847 * @param int $error Error number to set
25848 *
25849 * @return CurlHandle
25850 */
25851 public function setErrorNo($error)
25852 {
25853 $this->errorNo = $error;
25854
25855 return $this;
25856 }
25857
25858 /**
25859 * Get cURL curl_getinfo data
25860 *
25861 * @param int $option Option to retrieve. Pass null to retrieve all data as an array.
25862 *
25863 * @return array|mixed
25864 */
25865 public function getInfo($option = null)
25866 {
25867 if (!is_resource($this->handle)) {
25868 return null;
25869 }
25870
25871 if (null !== $option) {
25872 return curl_getinfo($this->handle, $option) ?: null;
25873 }
25874
25875 return curl_getinfo($this->handle) ?: array();
25876 }
25877
25878 /**
25879 * Get the stderr output
25880 *
25881 * @param bool $asResource Set to TRUE to get an fopen resource
25882 *
25883 * @return string|resource|null
25884 */
25885 public function getStderr($asResource = false)
25886 {
25887 $stderr = $this->getOptions()->get(CURLOPT_STDERR);
25888 if (!$stderr) {
25889 return null;
25890 }
25891
25892 if ($asResource) {
25893 return $stderr;
25894 }
25895
25896 fseek($stderr, 0);
25897 $e = stream_get_contents($stderr);
25898 fseek($stderr, 0, SEEK_END);
25899
25900 return $e;
25901 }
25902
25903 /**
25904 * Get the URL that this handle is connecting to
25905 *
25906 * @return Url
25907 */
25908 public function getUrl()
25909 {
25910 return Url::factory($this->options->get(CURLOPT_URL));
25911 }
25912
25913 /**
25914 * Get the wrapped curl handle
25915 *
25916 * @return resource|null Returns the cURL handle or null if it was closed
25917 */
25918 public function getHandle()
25919 {
25920 return $this->isAvailable() ? $this->handle : null;
25921 }
25922
25923 /**
25924 * Get the cURL setopt options of the handle. Changing values in the return object will have no effect on the curl
25925 * handle after it is created.
25926 *
25927 * @return Collection
25928 */
25929 public function getOptions()
25930 {
25931 return $this->options;
25932 }
25933
25934 /**
25935 * Update a request based on the log messages of the CurlHandle
25936 *
25937 * @param RequestInterface $request Request to update
25938 */
25939 public function updateRequestFromTransfer(RequestInterface $request)
25940 {
25941 if (!$request->getResponse()) {
25942 return;
25943 }
25944
25945 // Update the transfer stats of the response
25946 $request->getResponse()->setInfo($this->getInfo());
25947
25948 if (!$log = $this->getStderr(true)) {
25949 return;
25950 }
25951
25952 // Parse the cURL stderr output for outgoing requests
25953 $headers = '';
25954 fseek($log, 0);
25955 while (($line = fgets($log)) !== false) {
25956 if ($line && $line[0] == '>') {
25957 $headers = substr(trim($line), 2) . "\r\n";
25958 while (($line = fgets($log)) !== false) {
25959 if ($line[0] == '*' || $line[0] == '<') {
25960 break;
25961 } else {
25962 $headers .= trim($line) . "\r\n";
25963 }
25964 }
25965 }
25966 }
25967
25968 // Add request headers to the request exactly as they were sent
25969 if ($headers) {
25970 $parsed = ParserRegistry::getInstance()->getParser('message')->parseRequest($headers);
25971 if (!empty($parsed['headers'])) {
25972 $request->setHeaders(array());
25973 foreach ($parsed['headers'] as $name => $value) {
25974 $request->setHeader($name, $value);
25975 }
25976 }
25977 if (!empty($parsed['version'])) {
25978 $request->setProtocolVersion($parsed['version']);
25979 }
25980 }
25981 }
25982
25983 /**
25984 * Parse the config and replace curl.* configurators into the constant based values so it can be used elsewhere
25985 *
25986 * @param array|Collection $config The configuration we want to parse
25987 *
25988 * @return array
25989 */
25990 public static function parseCurlConfig($config)
25991 {
25992 $curlOptions = array();
25993 foreach ($config as $key => $value) {
25994 if (is_string($key) && defined($key)) {
25995 // Convert constants represented as string to constant int values
25996 $key = constant($key);
25997 }
25998 if (is_string($value) && defined($value)) {
25999 $value = constant($value);
26000 }
26001 $curlOptions[$key] = $value;
26002 }
26003
26004 return $curlOptions;
26005 }
26006 }
26007 <?php
26008
26009 namespace Guzzle\Http\Curl;
26010
26011 use Guzzle\Common\HasDispatcherInterface;
26012 use Guzzle\Common\Exception\ExceptionCollection;
26013 use Guzzle\Http\Message\RequestInterface;
26014
26015 /**
26016 * Interface for sending a pool of {@see RequestInterface} objects in parallel
26017 */
26018 interface CurlMultiInterface extends \Countable, HasDispatcherInterface
26019 {
26020 const POLLING_REQUEST = 'curl_multi.polling_request';
26021 const ADD_REQUEST = 'curl_multi.add_request';
26022 const REMOVE_REQUEST = 'curl_multi.remove_request';
26023 const MULTI_EXCEPTION = 'curl_multi.exception';
26024 const BLOCKING = 'curl_multi.blocking';
26025
26026 /**
26027 * Add a request to the pool.
26028 *
26029 * @param RequestInterface $request Request to add
26030 *
26031 * @return CurlMultiInterface
26032 */
26033 public function add(RequestInterface $request);
26034
26035 /**
26036 * Get an array of attached {@see RequestInterface} objects
26037 *
26038 * @return array
26039 */
26040 public function all();
26041
26042 /**
26043 * Remove a request from the pool.
26044 *
26045 * @param RequestInterface $request Request to remove
26046 *
26047 * @return bool Returns true on success or false on failure
26048 */
26049 public function remove(RequestInterface $request);
26050
26051 /**
26052 * Reset the state and remove any attached RequestInterface objects
26053 *
26054 * @param bool $hard Set to true to close and reopen any open multi handles
26055 */
26056 public function reset($hard = false);
26057
26058 /**
26059 * Send a pool of {@see RequestInterface} requests.
26060 *
26061 * @throws ExceptionCollection if any requests threw exceptions during the transfer.
26062 */
26063 public function send();
26064 }
26065 <?php
26066
26067 namespace Guzzle\Http\Curl;
26068
26069 /**
26070 * Class used for querying curl_version data
26071 */
26072 class CurlVersion
26073 {
26074 /** @var array curl_version() information */
26075 protected $version;
26076
26077 /** @var CurlVersion */
26078 protected static $instance;
26079
26080 /** @var string Default user agent */
26081 protected $userAgent;
26082
26083 /**
26084 * @return CurlVersion
26085 */
26086 public static function getInstance()
26087 {
26088 if (!self::$instance) {
26089 self::$instance = new self();
26090 }
26091
26092 return self::$instance;
26093 }
26094
26095 /**
26096 * Get all of the curl_version() data
26097 *
26098 * @return array
26099 */
26100 public function getAll()
26101 {
26102 if (!$this->version) {
26103 $this->version = curl_version();
26104 }
26105
26106 return $this->version;
26107 }
26108
26109 /**
26110 * Get a specific type of curl information
26111 *
26112 * @param string $type Version information to retrieve. This value is one of:
26113 * - version_number: cURL 24 bit version number
26114 * - version: cURL version number, as a string
26115 * - ssl_version_number: OpenSSL 24 bit version number
26116 * - ssl_version: OpenSSL version number, as a string
26117 * - libz_version: zlib version number, as a string
26118 * - host: Information about the host where cURL was built
26119 * - features: A bitmask of the CURL_VERSION_XXX constants
26120 * - protocols: An array of protocols names supported by cURL
26121 *
26122 * @return string|float|bool if the $type is found, and false if not found
26123 */
26124 public function get($type)
26125 {
26126 $version = $this->getAll();
26127
26128 return isset($version[$type]) ? $version[$type] : false;
26129 }
26130 }
26131 <?php
26132
26133 namespace Guzzle\Http\Curl;
26134
26135 use Guzzle\Common\AbstractHasDispatcher;
26136 use Guzzle\Http\Message\RequestInterface;
26137
26138 /**
26139 * Proxies requests and connections to a pool of internal curl_multi handles. Each recursive call will add requests
26140 * to the next available CurlMulti handle.
26141 */
26142 class CurlMultiProxy extends AbstractHasDispatcher implements CurlMultiInterface
26143 {
26144 protected $handles = array();
26145 protected $groups = array();
26146 protected $queued = array();
26147 protected $maxHandles;
26148
26149 /**
26150 * @param int $maxHandles The maximum number of idle CurlMulti handles to allow to remain open
26151 */
26152 public function __construct($maxHandles = 3)
26153 {
26154 $this->maxHandles = $maxHandles;
26155 // You can get some weird "Too many open files" errors when sending a large amount of requests in parallel.
26156 // These two statements autoload classes before a system runs out of file descriptors so that you can get back
26157 // valuable error messages if you run out.
26158 class_exists('Guzzle\Http\Message\Response');
26159 class_exists('Guzzle\Http\Exception\CurlException');
26160 }
26161
26162 public function add(RequestInterface $request)
26163 {
26164 $this->queued[] = $request;
26165
26166 return $this;
26167 }
26168
26169 public function all()
26170 {
26171 $requests = $this->queued;
26172 foreach ($this->handles as $handle) {
26173 $requests = array_merge($requests, $handle->all());
26174 }
26175
26176 return $requests;
26177 }
26178
26179 public function remove(RequestInterface $request)
26180 {
26181 foreach ($this->queued as $i => $r) {
26182 if ($request === $r) {
26183 unset($this->queued[$i]);
26184 return true;
26185 }
26186 }
26187
26188 foreach ($this->handles as $handle) {
26189 if ($handle->remove($request)) {
26190 return true;
26191 }
26192 }
26193
26194 return false;
26195 }
26196
26197 public function reset($hard = false)
26198 {
26199 $this->queued = array();
26200 $this->groups = array();
26201 foreach ($this->handles as $handle) {
26202 $handle->reset();
26203 }
26204 if ($hard) {
26205 $this->handles = array();
26206 }
26207
26208 return $this;
26209 }
26210
26211 public function send()
26212 {
26213 if ($this->queued) {
26214 $group = $this->getAvailableHandle();
26215 // Add this handle to a list of handles than is claimed
26216 $this->groups[] = $group;
26217 while ($request = array_shift($this->queued)) {
26218 $group->add($request);
26219 }
26220 try {
26221 $group->send();
26222 array_pop($this->groups);
26223 $this->cleanupHandles();
26224 } catch (\Exception $e) {
26225 // Remove the group and cleanup if an exception was encountered and no more requests in group
26226 if (!$group->count()) {
26227 array_pop($this->groups);
26228 $this->cleanupHandles();
26229 }
26230 throw $e;
26231 }
26232 }
26233 }
26234
26235 public function count()
26236 {
26237 return count($this->all());
26238 }
26239
26240 /**
26241 * Get an existing available CurlMulti handle or create a new one
26242 *
26243 * @return CurlMulti
26244 */
26245 protected function getAvailableHandle()
26246 {
26247 // Grab a handle that is not claimed
26248 foreach ($this->handles as $h) {
26249 if (!in_array($h, $this->groups, true)) {
26250 return $h;
26251 }
26252 }
26253
26254 // All are claimed, so create one
26255 $handle = new CurlMulti();
26256 $handle->setEventDispatcher($this->getEventDispatcher());
26257 $this->handles[] = $handle;
26258
26259 return $handle;
26260 }
26261
26262 /**
26263 * Trims down unused CurlMulti handles to limit the number of open connections
26264 */
26265 protected function cleanupHandles()
26266 {
26267 if ($diff = max(0, count($this->handles) - $this->maxHandles)) {
26268 for ($i = count($this->handles) - 1; $i > 0 && $diff > 0; $i--) {
26269 if (!count($this->handles[$i])) {
26270 unset($this->handles[$i]);
26271 $diff--;
26272 }
26273 }
26274 $this->handles = array_values($this->handles);
26275 }
26276 }
26277 }
26278 <?php
26279
26280 namespace Guzzle\Http\Curl;
26281
26282 use Guzzle\Http\Message\RequestInterface;
26283 use Guzzle\Http\EntityBody;
26284 use Guzzle\Http\Message\Response;
26285
26286 /**
26287 * Mediator between curl handles and request objects
26288 */
26289 class RequestMediator
26290 {
26291 /** @var RequestInterface */
26292 protected $request;
26293
26294 /** @var bool Whether or not to emit read/write events */
26295 protected $emitIo;
26296
26297 /**
26298 * @param RequestInterface $request Request to mediate
26299 * @param bool $emitIo Set to true to dispatch events on input and output
26300 */
26301 public function __construct(RequestInterface $request, $emitIo = false)
26302 {
26303 $this->request = $request;
26304 $this->emitIo = $emitIo;
26305 }
26306
26307 /**
26308 * Receive a response header from curl
26309 *
26310 * @param resource $curl Curl handle
26311 * @param string $header Received header
26312 *
26313 * @return int
26314 */
26315 public function receiveResponseHeader($curl, $header)
26316 {
26317 static $normalize = array("\r", "\n");
26318 $length = strlen($header);
26319 $header = str_replace($normalize, '', $header);
26320
26321 if (strpos($header, 'HTTP/') === 0) {
26322
26323 $startLine = explode(' ', $header, 3);
26324 $code = $startLine[1];
26325 $status = isset($startLine[2]) ? $startLine[2] : '';
26326
26327 // Only download the body of the response to the specified response
26328 // body when a successful response is received.
26329 if ($code >= 200 && $code < 300) {
26330 $body = $this->request->getResponseBody();
26331 } else {
26332 $body = EntityBody::factory();
26333 }
26334
26335 $response = new Response($code, null, $body);
26336 $response->setStatus($code, $status);
26337 $this->request->startResponse($response);
26338
26339 $this->request->dispatch('request.receive.status_line', array(
26340 'request' => $this,
26341 'line' => $header,
26342 'status_code' => $code,
26343 'reason_phrase' => $status
26344 ));
26345
26346 } elseif ($pos = strpos($header, ':')) {
26347 $this->request->getResponse()->addHeader(
26348 trim(substr($header, 0, $pos)),
26349 trim(substr($header, $pos + 1))
26350 );
26351 }
26352
26353 return $length;
26354 }
26355
26356 /**
26357 * Received a progress notification
26358 *
26359 * @param int $downloadSize Total download size
26360 * @param int $downloaded Amount of bytes downloaded
26361 * @param int $uploadSize Total upload size
26362 * @param int $uploaded Amount of bytes uploaded
26363 * @param resource $handle CurlHandle object
26364 */
26365 public function progress($downloadSize, $downloaded, $uploadSize, $uploaded, $handle = null)
26366 {
26367 $this->request->dispatch('curl.callback.progress', array(
26368 'request' => $this->request,
26369 'handle' => $handle,
26370 'download_size' => $downloadSize,
26371 'downloaded' => $downloaded,
26372 'upload_size' => $uploadSize,
26373 'uploaded' => $uploaded
26374 ));
26375 }
26376
26377 /**
26378 * Write data to the response body of a request
26379 *
26380 * @param resource $curl Curl handle
26381 * @param string $write Data that was received
26382 *
26383 * @return int
26384 */
26385 public function writeResponseBody($curl, $write)
26386 {
26387 if ($this->emitIo) {
26388 $this->request->dispatch('curl.callback.write', array(
26389 'request' => $this->request,
26390 'write' => $write
26391 ));
26392 }
26393
26394 if ($response = $this->request->getResponse()) {
26395 return $response->getBody()->write($write);
26396 } else {
26397 // Unexpected data received before response headers - abort transfer
26398 return 0;
26399 }
26400 }
26401
26402 /**
26403 * Read data from the request body and send it to curl
26404 *
26405 * @param resource $ch Curl handle
26406 * @param resource $fd File descriptor
26407 * @param int $length Amount of data to read
26408 *
26409 * @return string
26410 */
26411 public function readRequestBody($ch, $fd, $length)
26412 {
26413 if (!($body = $this->request->getBody())) {
26414 return '';
26415 }
26416
26417 $read = (string) $body->read($length);
26418 if ($this->emitIo) {
26419 $this->request->dispatch('curl.callback.read', array('request' => $this->request, 'read' => $read));
26420 }
26421
26422 return $read;
26423 }
26424 }
26425 <?php
26426
26427 namespace Guzzle\Http\Curl;
26428
26429 use Guzzle\Common\AbstractHasDispatcher;
26430 use Guzzle\Common\Event;
26431 use Guzzle\Http\Exception\MultiTransferException;
26432 use Guzzle\Http\Exception\CurlException;
26433 use Guzzle\Http\Message\RequestInterface;
26434
26435 /**
26436 * Send {@see RequestInterface} objects in parallel using curl_multi
26437 */
26438 class CurlMulti extends AbstractHasDispatcher implements CurlMultiInterface
26439 {
26440 /** @var resource cURL multi handle. */
26441 protected $multiHandle;
26442
26443 /** @var array Attached {@see RequestInterface} objects. */
26444 protected $requests;
26445
26446 /** @var \SplObjectStorage RequestInterface to CurlHandle hash */
26447 protected $handles;
26448
26449 /** @var array Hash mapping curl handle resource IDs to request objects */
26450 protected $resourceHash;
26451
26452 /** @var array Queued exceptions */
26453 protected $exceptions = array();
26454
26455 /** @var array Requests that succeeded */
26456 protected $successful = array();
26457
26458 /** @var array cURL multi error values and codes */
26459 protected $multiErrors = array(
26460 CURLM_BAD_HANDLE => array('CURLM_BAD_HANDLE', 'The passed-in handle is not a valid CURLM handle.'),
26461 CURLM_BAD_EASY_HANDLE => array('CURLM_BAD_EASY_HANDLE', "An easy handle was not good/valid. It could mean that it isn't an easy handle at all, or possibly that the handle already is in used by this or another multi handle."),
26462 CURLM_OUT_OF_MEMORY => array('CURLM_OUT_OF_MEMORY', 'You are doomed.'),
26463 CURLM_INTERNAL_ERROR => array('CURLM_INTERNAL_ERROR', 'This can only be returned if libcurl bugs. Please report it to us!')
26464 );
26465
26466 public function __construct()
26467 {
26468 $this->multiHandle = curl_multi_init();
26469 // @codeCoverageIgnoreStart
26470 if ($this->multiHandle === false) {
26471 throw new CurlException('Unable to create multi handle');
26472 }
26473 // @codeCoverageIgnoreEnd
26474 $this->reset();
26475 }
26476
26477 public function __destruct()
26478 {
26479 if (is_resource($this->multiHandle)) {
26480 curl_multi_close($this->multiHandle);
26481 }
26482 }
26483
26484 public function add(RequestInterface $request)
26485 {
26486 $this->requests[] = $request;
26487 // If requests are currently transferring and this is async, then the
26488 // request must be prepared now as the send() method is not called.
26489 $this->beforeSend($request);
26490 $this->dispatch(self::ADD_REQUEST, array('request' => $request));
26491
26492 return $this;
26493 }
26494
26495 public function all()
26496 {
26497 return $this->requests;
26498 }
26499
26500 public function remove(RequestInterface $request)
26501 {
26502 $this->removeHandle($request);
26503 if (($index = array_search($request, $this->requests, true)) !== false) {
26504 $request = $this->requests[$index];
26505 unset($this->requests[$index]);
26506 $this->requests = array_values($this->requests);
26507 $this->dispatch(self::REMOVE_REQUEST, array('request' => $request));
26508 return true;
26509 }
26510
26511 return false;
26512 }
26513
26514 public function reset($hard = false)
26515 {
26516 // Remove each request
26517 if ($this->requests) {
26518 foreach ($this->requests as $request) {
26519 $this->remove($request);
26520 }
26521 }
26522
26523 $this->handles = new \SplObjectStorage();
26524 $this->requests = $this->resourceHash = $this->exceptions = $this->successful = array();
26525 }
26526
26527 public function send()
26528 {
26529 $this->perform();
26530 $exceptions = $this->exceptions;
26531 $successful = $this->successful;
26532 $this->reset();
26533
26534 if ($exceptions) {
26535 $this->throwMultiException($exceptions, $successful);
26536 }
26537 }
26538
26539 public function count()
26540 {
26541 return count($this->requests);
26542 }
26543
26544 /**
26545 * Build and throw a MultiTransferException
26546 *
26547 * @param array $exceptions Exceptions encountered
26548 * @param array $successful Successful requests
26549 * @throws MultiTransferException
26550 */
26551 protected function throwMultiException(array $exceptions, array $successful)
26552 {
26553 $multiException = new MultiTransferException('Errors during multi transfer');
26554
26555 while ($e = array_shift($exceptions)) {
26556 $multiException->addFailedRequestWithException($e['request'], $e['exception']);
26557 }
26558
26559 // Add successful requests
26560 foreach ($successful as $request) {
26561 if (!$multiException->containsRequest($request)) {
26562 $multiException->addSuccessfulRequest($request);
26563 }
26564 }
26565
26566 throw $multiException;
26567 }
26568
26569 /**
26570 * Prepare for sending
26571 *
26572 * @param RequestInterface $request Request to prepare
26573 * @throws \Exception on error preparing the request
26574 */
26575 protected function beforeSend(RequestInterface $request)
26576 {
26577 try {
26578 $state = $request->setState(RequestInterface::STATE_TRANSFER);
26579 if ($state == RequestInterface::STATE_TRANSFER) {
26580 // Add the request curl handle to the multi handle
26581 $handle = $this->createCurlHandle($request)->getHandle();
26582 $this->checkCurlResult(curl_multi_add_handle($this->multiHandle, $handle));
26583 } else {
26584 // Requests might decide they don't need to be sent just before transfer (e.g. CachePlugin)
26585 $this->remove($request);
26586 if ($state == RequestInterface::STATE_COMPLETE) {
26587 $this->successful[] = $request;
26588 }
26589 }
26590 } catch (\Exception $e) {
26591 // Queue the exception to be thrown when sent
26592 $this->removeErroredRequest($request, $e);
26593 }
26594 }
26595
26596 /**
26597 * Create a curl handle for a request
26598 *
26599 * @param RequestInterface $request Request
26600 *
26601 * @return CurlHandle
26602 */
26603 protected function createCurlHandle(RequestInterface $request)
26604 {
26605 $wrapper = CurlHandle::factory($request);
26606 $this->handles[$request] = $wrapper;
26607 $this->resourceHash[(int) $wrapper->getHandle()] = $request;
26608
26609 return $wrapper;
26610 }
26611
26612 /**
26613 * Get the data from the multi handle
26614 */
26615 protected function perform()
26616 {
26617 $event = new Event(array('curl_multi' => $this));
26618
26619 while ($this->requests) {
26620 // Notify each request as polling
26621 $blocking = $total = 0;
26622 foreach ($this->requests as $request) {
26623 ++$total;
26624 $event['request'] = $request;
26625 $request->getEventDispatcher()->dispatch(self::POLLING_REQUEST, $event);
26626 // The blocking variable just has to be non-falsey to block the loop
26627 if ($request->getParams()->hasKey(self::BLOCKING)) {
26628 ++$blocking;
26629 }
26630 }
26631 if ($blocking == $total) {
26632 // Sleep to prevent eating CPU because no requests are actually pending a select call
26633 usleep(500);
26634 } else {
26635 $this->executeHandles();
26636 }
26637 }
26638 }
26639
26640 /**
26641 * Execute and select curl handles
26642 */
26643 private function executeHandles()
26644 {
26645 // The first curl_multi_select often times out no matter what, but is usually required for fast transfers
26646 $selectTimeout = 0.001;
26647 $active = false;
26648 do {
26649 while (($mrc = curl_multi_exec($this->multiHandle, $active)) == CURLM_CALL_MULTI_PERFORM);
26650 $this->checkCurlResult($mrc);
26651 $this->processMessages();
26652 if ($active && curl_multi_select($this->multiHandle, $selectTimeout) === -1) {
26653 // Perform a usleep if a select returns -1: https://bugs.php.net/bug.php?id=61141
26654 usleep(150);
26655 }
26656 $selectTimeout = 1;
26657 } while ($active);
26658 }
26659
26660 /**
26661 * Process any received curl multi messages
26662 */
26663 private function processMessages()
26664 {
26665 while ($done = curl_multi_info_read($this->multiHandle)) {
26666 $request = $this->resourceHash[(int) $done['handle']];
26667 try {
26668 $this->processResponse($request, $this->handles[$request], $done);
26669 $this->successful[] = $request;
26670 } catch (\Exception $e) {
26671 $this->removeErroredRequest($request, $e);
26672 }
26673 }
26674 }
26675
26676 /**
26677 * Remove a request that encountered an exception
26678 *
26679 * @param RequestInterface $request Request to remove
26680 * @param \Exception $e Exception encountered
26681 */
26682 protected function removeErroredRequest(RequestInterface $request, \Exception $e = null)
26683 {
26684 $this->exceptions[] = array('request' => $request, 'exception' => $e);
26685 $this->remove($request);
26686 $this->dispatch(self::MULTI_EXCEPTION, array('exception' => $e, 'all_exceptions' => $this->exceptions));
26687 }
26688
26689 /**
26690 * Check for errors and fix headers of a request based on a curl response
26691 *
26692 * @param RequestInterface $request Request to process
26693 * @param CurlHandle $handle Curl handle object
26694 * @param array $curl Array returned from curl_multi_info_read
26695 *
26696 * @throws CurlException on Curl error
26697 */
26698 protected function processResponse(RequestInterface $request, CurlHandle $handle, array $curl)
26699 {
26700 // Set the transfer stats on the response
26701 $handle->updateRequestFromTransfer($request);
26702 // Check if a cURL exception occurred, and if so, notify things
26703 $curlException = $this->isCurlException($request, $handle, $curl);
26704
26705 // Always remove completed curl handles. They can be added back again
26706 // via events if needed (e.g. ExponentialBackoffPlugin)
26707 $this->removeHandle($request);
26708
26709 if (!$curlException) {
26710 $state = $request->setState(RequestInterface::STATE_COMPLETE, array('handle' => $handle));
26711 // Only remove the request if it wasn't resent as a result of the state change
26712 if ($state != RequestInterface::STATE_TRANSFER) {
26713 $this->remove($request);
26714 }
26715 } else {
26716 // Set the state of the request to an error
26717 $state = $request->setState(RequestInterface::STATE_ERROR, array('exception' => $curlException));
26718 // Allow things to ignore the error if possible
26719 if ($state != RequestInterface::STATE_TRANSFER) {
26720 $this->remove($request);
26721 }
26722 // The error was not handled, so fail
26723 if ($state == RequestInterface::STATE_ERROR) {
26724 /** @var CurlException $curlException */
26725 throw $curlException;
26726 }
26727 }
26728 }
26729
26730 /**
26731 * Remove a curl handle from the curl multi object
26732 *
26733 * @param RequestInterface $request Request that owns the handle
26734 */
26735 protected function removeHandle(RequestInterface $request)
26736 {
26737 if (isset($this->handles[$request])) {
26738 $handle = $this->handles[$request];
26739 curl_multi_remove_handle($this->multiHandle, $handle->getHandle());
26740 unset($this->handles[$request]);
26741 unset($this->resourceHash[(int) $handle->getHandle()]);
26742 $handle->close();
26743 }
26744 }
26745
26746 /**
26747 * Check if a cURL transfer resulted in what should be an exception
26748 *
26749 * @param RequestInterface $request Request to check
26750 * @param CurlHandle $handle Curl handle object
26751 * @param array $curl Array returned from curl_multi_info_read
26752 *
26753 * @return CurlException|bool
26754 */
26755 private function isCurlException(RequestInterface $request, CurlHandle $handle, array $curl)
26756 {
26757 if (CURLM_OK == $curl['result'] || CURLM_CALL_MULTI_PERFORM == $curl['result']) {
26758 return false;
26759 }
26760
26761 $handle->setErrorNo($curl['result']);
26762 $e = new CurlException(sprintf('[curl] %s: %s [url] %s',
26763 $handle->getErrorNo(), $handle->getError(), $handle->getUrl()));
26764 $e->setCurlHandle($handle)
26765 ->setRequest($request)
26766 ->setCurlInfo($handle->getInfo())
26767 ->setError($handle->getError(), $handle->getErrorNo());
26768
26769 return $e;
26770 }
26771
26772 /**
26773 * Throw an exception for a cURL multi response if needed
26774 *
26775 * @param int $code Curl response code
26776 * @throws CurlException
26777 */
26778 private function checkCurlResult($code)
26779 {
26780 if ($code != CURLM_OK && $code != CURLM_CALL_MULTI_PERFORM) {
26781 throw new CurlException(isset($this->multiErrors[$code])
26782 ? "cURL error: {$code} ({$this->multiErrors[$code][0]}): cURL message: {$this->multiErrors[$code][1]}"
26783 : 'Unexpected cURL error: ' . $code
26784 );
26785 }
26786 }
26787 }
26788 <?php
26789
26790 namespace Guzzle\Http\QueryAggregator;
26791
26792 use Guzzle\Http\QueryString;
26793
26794 /**
26795 * Aggregates nested query string variables using commas
26796 */
26797 class CommaAggregator implements QueryAggregatorInterface
26798 {
26799 public function aggregate($key, $value, QueryString $query)
26800 {
26801 if ($query->isUrlEncoding()) {
26802 return array($query->encodeValue($key) => implode(',', array_map(array($query, 'encodeValue'), $value)));
26803 } else {
26804 return array($key => implode(',', $value));
26805 }
26806 }
26807 }
26808 <?php
26809
26810 namespace Guzzle\Http\QueryAggregator;
26811
26812 use Guzzle\Http\QueryString;
26813
26814 /**
26815 * Aggregates nested query string variables using PHP style []
26816 */
26817 class PhpAggregator implements QueryAggregatorInterface
26818 {
26819 public function aggregate($key, $value, QueryString $query)
26820 {
26821 $ret = array();
26822
26823 foreach ($value as $k => $v) {
26824 $k = "{$key}[{$k}]";
26825 if (is_array($v)) {
26826 $ret = array_merge($ret, self::aggregate($k, $v, $query));
26827 } else {
26828 $ret[$query->encodeValue($k)] = $query->encodeValue($v);
26829 }
26830 }
26831
26832 return $ret;
26833 }
26834 }
26835 <?php
26836
26837 namespace Guzzle\Http\QueryAggregator;
26838
26839 use Guzzle\Http\QueryString;
26840
26841 /**
26842 * Does not aggregate nested query string values and allows duplicates in the resulting array
26843 *
26844 * Example: http://test.com?q=1&q=2
26845 */
26846 class DuplicateAggregator implements QueryAggregatorInterface
26847 {
26848 public function aggregate($key, $value, QueryString $query)
26849 {
26850 if ($query->isUrlEncoding()) {
26851 return array($query->encodeValue($key) => array_map(array($query, 'encodeValue'), $value));
26852 } else {
26853 return array($key => $value);
26854 }
26855 }
26856 }
26857 <?php
26858
26859 namespace Guzzle\Http\QueryAggregator;
26860
26861 use Guzzle\Http\QueryString;
26862
26863 /**
26864 * Interface used for aggregating nested query string variables into a flattened array of key value pairs
26865 */
26866 interface QueryAggregatorInterface
26867 {
26868 /**
26869 * Aggregate multi-valued parameters into a flattened associative array
26870 *
26871 * @param string $key The name of the query string parameter
26872 * @param array $value The values of the parameter
26873 * @param QueryString $query The query string that is being aggregated
26874 *
26875 * @return array Returns an array of the combined values
26876 */
26877 public function aggregate($key, $value, QueryString $query);
26878 }
26879 <?php
26880
26881 namespace Guzzle\Http;
26882
26883 /**
26884 * Provides mappings of file extensions to mimetypes
26885 * @link http://svn.apache.org/repos/asf/httpd/httpd/branches/1.3.x/conf/mime.types
26886 */
26887 class Mimetypes
26888 {
26889 /** @var self */
26890 protected static $instance;
26891
26892 /** @var array Mapping of extension to mimetype */
26893 protected $mimetypes = array(
26894 '3dml' => 'text/vnd.in3d.3dml',
26895 '3g2' => 'video/3gpp2',
26896 '3gp' => 'video/3gpp',
26897 '7z' => 'application/x-7z-compressed',
26898 'aab' => 'application/x-authorware-bin',
26899 'aac' => 'audio/x-aac',
26900 'aam' => 'application/x-authorware-map',
26901 'aas' => 'application/x-authorware-seg',
26902 'abw' => 'application/x-abiword',
26903 'ac' => 'application/pkix-attr-cert',
26904 'acc' => 'application/vnd.americandynamics.acc',
26905 'ace' => 'application/x-ace-compressed',
26906 'acu' => 'application/vnd.acucobol',
26907 'acutc' => 'application/vnd.acucorp',
26908 'adp' => 'audio/adpcm',
26909 'aep' => 'application/vnd.audiograph',
26910 'afm' => 'application/x-font-type1',
26911 'afp' => 'application/vnd.ibm.modcap',
26912 'ahead' => 'application/vnd.ahead.space',
26913 'ai' => 'application/postscript',
26914 'aif' => 'audio/x-aiff',
26915 'aifc' => 'audio/x-aiff',
26916 'aiff' => 'audio/x-aiff',
26917 'air' => 'application/vnd.adobe.air-application-installer-package+zip',
26918 'ait' => 'application/vnd.dvb.ait',
26919 'ami' => 'application/vnd.amiga.ami',
26920 'apk' => 'application/vnd.android.package-archive',
26921 'application' => 'application/x-ms-application',
26922 'apr' => 'application/vnd.lotus-approach',
26923 'asa' => 'text/plain',
26924 'asax' => 'application/octet-stream',
26925 'asc' => 'application/pgp-signature',
26926 'ascx' => 'text/plain',
26927 'asf' => 'video/x-ms-asf',
26928 'ashx' => 'text/plain',
26929 'asm' => 'text/x-asm',
26930 'asmx' => 'text/plain',
26931 'aso' => 'application/vnd.accpac.simply.aso',
26932 'asp' => 'text/plain',
26933 'aspx' => 'text/plain',
26934 'asx' => 'video/x-ms-asf',
26935 'atc' => 'application/vnd.acucorp',
26936 'atom' => 'application/atom+xml',
26937 'atomcat' => 'application/atomcat+xml',
26938 'atomsvc' => 'application/atomsvc+xml',
26939 'atx' => 'application/vnd.antix.game-component',
26940 'au' => 'audio/basic',
26941 'avi' => 'video/x-msvideo',
26942 'aw' => 'application/applixware',
26943 'axd' => 'text/plain',
26944 'azf' => 'application/vnd.airzip.filesecure.azf',
26945 'azs' => 'application/vnd.airzip.filesecure.azs',
26946 'azw' => 'application/vnd.amazon.ebook',
26947 'bat' => 'application/x-msdownload',
26948 'bcpio' => 'application/x-bcpio',
26949 'bdf' => 'application/x-font-bdf',
26950 'bdm' => 'application/vnd.syncml.dm+wbxml',
26951 'bed' => 'application/vnd.realvnc.bed',
26952 'bh2' => 'application/vnd.fujitsu.oasysprs',
26953 'bin' => 'application/octet-stream',
26954 'bmi' => 'application/vnd.bmi',
26955 'bmp' => 'image/bmp',
26956 'book' => 'application/vnd.framemaker',
26957 'box' => 'application/vnd.previewsystems.box',
26958 'boz' => 'application/x-bzip2',
26959 'bpk' => 'application/octet-stream',
26960 'btif' => 'image/prs.btif',
26961 'bz' => 'application/x-bzip',
26962 'bz2' => 'application/x-bzip2',
26963 'c' => 'text/x-c',
26964 'c11amc' => 'application/vnd.cluetrust.cartomobile-config',
26965 'c11amz' => 'application/vnd.cluetrust.cartomobile-config-pkg',
26966 'c4d' => 'application/vnd.clonk.c4group',
26967 'c4f' => 'application/vnd.clonk.c4group',
26968 'c4g' => 'application/vnd.clonk.c4group',
26969 'c4p' => 'application/vnd.clonk.c4group',
26970 'c4u' => 'application/vnd.clonk.c4group',
26971 'cab' => 'application/vnd.ms-cab-compressed',
26972 'car' => 'application/vnd.curl.car',
26973 'cat' => 'application/vnd.ms-pki.seccat',
26974 'cc' => 'text/x-c',
26975 'cct' => 'application/x-director',
26976 'ccxml' => 'application/ccxml+xml',
26977 'cdbcmsg' => 'application/vnd.contact.cmsg',
26978 'cdf' => 'application/x-netcdf',
26979 'cdkey' => 'application/vnd.mediastation.cdkey',
26980 'cdmia' => 'application/cdmi-capability',
26981 'cdmic' => 'application/cdmi-container',
26982 'cdmid' => 'application/cdmi-domain',
26983 'cdmio' => 'application/cdmi-object',
26984 'cdmiq' => 'application/cdmi-queue',
26985 'cdx' => 'chemical/x-cdx',
26986 'cdxml' => 'application/vnd.chemdraw+xml',
26987 'cdy' => 'application/vnd.cinderella',
26988 'cer' => 'application/pkix-cert',
26989 'cfc' => 'application/x-coldfusion',
26990 'cfm' => 'application/x-coldfusion',
26991 'cgm' => 'image/cgm',
26992 'chat' => 'application/x-chat',
26993 'chm' => 'application/vnd.ms-htmlhelp',
26994 'chrt' => 'application/vnd.kde.kchart',
26995 'cif' => 'chemical/x-cif',
26996 'cii' => 'application/vnd.anser-web-certificate-issue-initiation',
26997 'cil' => 'application/vnd.ms-artgalry',
26998 'cla' => 'application/vnd.claymore',
26999 'class' => 'application/java-vm',
27000 'clkk' => 'application/vnd.crick.clicker.keyboard',
27001 'clkp' => 'application/vnd.crick.clicker.palette',
27002 'clkt' => 'application/vnd.crick.clicker.template',
27003 'clkw' => 'application/vnd.crick.clicker.wordbank',
27004 'clkx' => 'application/vnd.crick.clicker',
27005 'clp' => 'application/x-msclip',
27006 'cmc' => 'application/vnd.cosmocaller',
27007 'cmdf' => 'chemical/x-cmdf',
27008 'cml' => 'chemical/x-cml',
27009 'cmp' => 'application/vnd.yellowriver-custom-menu',
27010 'cmx' => 'image/x-cmx',
27011 'cod' => 'application/vnd.rim.cod',
27012 'com' => 'application/x-msdownload',
27013 'conf' => 'text/plain',
27014 'cpio' => 'application/x-cpio',
27015 'cpp' => 'text/x-c',
27016 'cpt' => 'application/mac-compactpro',
27017 'crd' => 'application/x-mscardfile',
27018 'crl' => 'application/pkix-crl',
27019 'crt' => 'application/x-x509-ca-cert',
27020 'cryptonote' => 'application/vnd.rig.cryptonote',
27021 'cs' => 'text/plain',
27022 'csh' => 'application/x-csh',
27023 'csml' => 'chemical/x-csml',
27024 'csp' => 'application/vnd.commonspace',
27025 'css' => 'text/css',
27026 'cst' => 'application/x-director',
27027 'csv' => 'text/csv',
27028 'cu' => 'application/cu-seeme',
27029 'curl' => 'text/vnd.curl',
27030 'cww' => 'application/prs.cww',
27031 'cxt' => 'application/x-director',
27032 'cxx' => 'text/x-c',
27033 'dae' => 'model/vnd.collada+xml',
27034 'daf' => 'application/vnd.mobius.daf',
27035 'dataless' => 'application/vnd.fdsn.seed',
27036 'davmount' => 'application/davmount+xml',
27037 'dcr' => 'application/x-director',
27038 'dcurl' => 'text/vnd.curl.dcurl',
27039 'dd2' => 'application/vnd.oma.dd2+xml',
27040 'ddd' => 'application/vnd.fujixerox.ddd',
27041 'deb' => 'application/x-debian-package',
27042 'def' => 'text/plain',
27043 'deploy' => 'application/octet-stream',
27044 'der' => 'application/x-x509-ca-cert',
27045 'dfac' => 'application/vnd.dreamfactory',
27046 'dic' => 'text/x-c',
27047 'dir' => 'application/x-director',
27048 'dis' => 'application/vnd.mobius.dis',
27049 'dist' => 'application/octet-stream',
27050 'distz' => 'application/octet-stream',
27051 'djv' => 'image/vnd.djvu',
27052 'djvu' => 'image/vnd.djvu',
27053 'dll' => 'application/x-msdownload',
27054 'dmg' => 'application/octet-stream',
27055 'dms' => 'application/octet-stream',
27056 'dna' => 'application/vnd.dna',
27057 'doc' => 'application/msword',
27058 'docm' => 'application/vnd.ms-word.document.macroenabled.12',
27059 'docx' => 'application/vnd.openxmlformats-officedocument.wordprocessingml.document',
27060 'dot' => 'application/msword',
27061 'dotm' => 'application/vnd.ms-word.template.macroenabled.12',
27062 'dotx' => 'application/vnd.openxmlformats-officedocument.wordprocessingml.template',
27063 'dp' => 'application/vnd.osgi.dp',
27064 'dpg' => 'application/vnd.dpgraph',
27065 'dra' => 'audio/vnd.dra',
27066 'dsc' => 'text/prs.lines.tag',
27067 'dssc' => 'application/dssc+der',
27068 'dtb' => 'application/x-dtbook+xml',
27069 'dtd' => 'application/xml-dtd',
27070 'dts' => 'audio/vnd.dts',
27071 'dtshd' => 'audio/vnd.dts.hd',
27072 'dump' => 'application/octet-stream',
27073 'dvi' => 'application/x-dvi',
27074 'dwf' => 'model/vnd.dwf',
27075 'dwg' => 'image/vnd.dwg',
27076 'dxf' => 'image/vnd.dxf',
27077 'dxp' => 'application/vnd.spotfire.dxp',
27078 'dxr' => 'application/x-director',
27079 'ecelp4800' => 'audio/vnd.nuera.ecelp4800',
27080 'ecelp7470' => 'audio/vnd.nuera.ecelp7470',
27081 'ecelp9600' => 'audio/vnd.nuera.ecelp9600',
27082 'ecma' => 'application/ecmascript',
27083 'edm' => 'application/vnd.novadigm.edm',
27084 'edx' => 'application/vnd.novadigm.edx',
27085 'efif' => 'application/vnd.picsel',
27086 'ei6' => 'application/vnd.pg.osasli',
27087 'elc' => 'application/octet-stream',
27088 'eml' => 'message/rfc822',
27089 'emma' => 'application/emma+xml',
27090 'eol' => 'audio/vnd.digital-winds',
27091 'eot' => 'application/vnd.ms-fontobject',
27092 'eps' => 'application/postscript',
27093 'epub' => 'application/epub+zip',
27094 'es3' => 'application/vnd.eszigno3+xml',
27095 'esf' => 'application/vnd.epson.esf',
27096 'et3' => 'application/vnd.eszigno3+xml',
27097 'etx' => 'text/x-setext',
27098 'exe' => 'application/x-msdownload',
27099 'exi' => 'application/exi',
27100 'ext' => 'application/vnd.novadigm.ext',
27101 'ez' => 'application/andrew-inset',
27102 'ez2' => 'application/vnd.ezpix-album',
27103 'ez3' => 'application/vnd.ezpix-package',
27104 'f' => 'text/x-fortran',
27105 'f4v' => 'video/x-f4v',
27106 'f77' => 'text/x-fortran',
27107 'f90' => 'text/x-fortran',
27108 'fbs' => 'image/vnd.fastbidsheet',
27109 'fcs' => 'application/vnd.isac.fcs',
27110 'fdf' => 'application/vnd.fdf',
27111 'fe_launch' => 'application/vnd.denovo.fcselayout-link',
27112 'fg5' => 'application/vnd.fujitsu.oasysgp',
27113 'fgd' => 'application/x-director',
27114 'fh' => 'image/x-freehand',
27115 'fh4' => 'image/x-freehand',
27116 'fh5' => 'image/x-freehand',
27117 'fh7' => 'image/x-freehand',
27118 'fhc' => 'image/x-freehand',
27119 'fig' => 'application/x-xfig',
27120 'fli' => 'video/x-fli',
27121 'flo' => 'application/vnd.micrografx.flo',
27122 'flv' => 'video/x-flv',
27123 'flw' => 'application/vnd.kde.kivio',
27124 'flx' => 'text/vnd.fmi.flexstor',
27125 'fly' => 'text/vnd.fly',
27126 'fm' => 'application/vnd.framemaker',
27127 'fnc' => 'application/vnd.frogans.fnc',
27128 'for' => 'text/x-fortran',
27129 'fpx' => 'image/vnd.fpx',
27130 'frame' => 'application/vnd.framemaker',
27131 'fsc' => 'application/vnd.fsc.weblaunch',
27132 'fst' => 'image/vnd.fst',
27133 'ftc' => 'application/vnd.fluxtime.clip',
27134 'fti' => 'application/vnd.anser-web-funds-transfer-initiation',
27135 'fvt' => 'video/vnd.fvt',
27136 'fxp' => 'application/vnd.adobe.fxp',
27137 'fxpl' => 'application/vnd.adobe.fxp',
27138 'fzs' => 'application/vnd.fuzzysheet',
27139 'g2w' => 'application/vnd.geoplan',
27140 'g3' => 'image/g3fax',
27141 'g3w' => 'application/vnd.geospace',
27142 'gac' => 'application/vnd.groove-account',
27143 'gdl' => 'model/vnd.gdl',
27144 'geo' => 'application/vnd.dynageo',
27145 'gex' => 'application/vnd.geometry-explorer',
27146 'ggb' => 'application/vnd.geogebra.file',
27147 'ggt' => 'application/vnd.geogebra.tool',
27148 'ghf' => 'application/vnd.groove-help',
27149 'gif' => 'image/gif',
27150 'gim' => 'application/vnd.groove-identity-message',
27151 'gmx' => 'application/vnd.gmx',
27152 'gnumeric' => 'application/x-gnumeric',
27153 'gph' => 'application/vnd.flographit',
27154 'gqf' => 'application/vnd.grafeq',
27155 'gqs' => 'application/vnd.grafeq',
27156 'gram' => 'application/srgs',
27157 'gre' => 'application/vnd.geometry-explorer',
27158 'grv' => 'application/vnd.groove-injector',
27159 'grxml' => 'application/srgs+xml',
27160 'gsf' => 'application/x-font-ghostscript',
27161 'gtar' => 'application/x-gtar',
27162 'gtm' => 'application/vnd.groove-tool-message',
27163 'gtw' => 'model/vnd.gtw',
27164 'gv' => 'text/vnd.graphviz',
27165 'gxt' => 'application/vnd.geonext',
27166 'h' => 'text/x-c',
27167 'h261' => 'video/h261',
27168 'h263' => 'video/h263',
27169 'h264' => 'video/h264',
27170 'hal' => 'application/vnd.hal+xml',
27171 'hbci' => 'application/vnd.hbci',
27172 'hdf' => 'application/x-hdf',
27173 'hh' => 'text/x-c',
27174 'hlp' => 'application/winhlp',
27175 'hpgl' => 'application/vnd.hp-hpgl',
27176 'hpid' => 'application/vnd.hp-hpid',
27177 'hps' => 'application/vnd.hp-hps',
27178 'hqx' => 'application/mac-binhex40',
27179 'hta' => 'application/octet-stream',
27180 'htc' => 'text/html',
27181 'htke' => 'application/vnd.kenameaapp',
27182 'htm' => 'text/html',
27183 'html' => 'text/html',
27184 'hvd' => 'application/vnd.yamaha.hv-dic',
27185 'hvp' => 'application/vnd.yamaha.hv-voice',
27186 'hvs' => 'application/vnd.yamaha.hv-script',
27187 'i2g' => 'application/vnd.intergeo',
27188 'icc' => 'application/vnd.iccprofile',
27189 'ice' => 'x-conference/x-cooltalk',
27190 'icm' => 'application/vnd.iccprofile',
27191 'ico' => 'image/x-icon',
27192 'ics' => 'text/calendar',
27193 'ief' => 'image/ief',
27194 'ifb' => 'text/calendar',
27195 'ifm' => 'application/vnd.shana.informed.formdata',
27196 'iges' => 'model/iges',
27197 'igl' => 'application/vnd.igloader',
27198 'igm' => 'application/vnd.insors.igm',
27199 'igs' => 'model/iges',
27200 'igx' => 'application/vnd.micrografx.igx',
27201 'iif' => 'application/vnd.shana.informed.interchange',
27202 'imp' => 'application/vnd.accpac.simply.imp',
27203 'ims' => 'application/vnd.ms-ims',
27204 'in' => 'text/plain',
27205 'ini' => 'text/plain',
27206 'ipfix' => 'application/ipfix',
27207 'ipk' => 'application/vnd.shana.informed.package',
27208 'irm' => 'application/vnd.ibm.rights-management',
27209 'irp' => 'application/vnd.irepository.package+xml',
27210 'iso' => 'application/octet-stream',
27211 'itp' => 'application/vnd.shana.informed.formtemplate',
27212 'ivp' => 'application/vnd.immervision-ivp',
27213 'ivu' => 'application/vnd.immervision-ivu',
27214 'jad' => 'text/vnd.sun.j2me.app-descriptor',
27215 'jam' => 'application/vnd.jam',
27216 'jar' => 'application/java-archive',
27217 'java' => 'text/x-java-source',
27218 'jisp' => 'application/vnd.jisp',
27219 'jlt' => 'application/vnd.hp-jlyt',
27220 'jnlp' => 'application/x-java-jnlp-file',
27221 'joda' => 'application/vnd.joost.joda-archive',
27222 'jpe' => 'image/jpeg',
27223 'jpeg' => 'image/jpeg',
27224 'jpg' => 'image/jpeg',
27225 'jpgm' => 'video/jpm',
27226 'jpgv' => 'video/jpeg',
27227 'jpm' => 'video/jpm',
27228 'js' => 'text/javascript',
27229 'json' => 'application/json',
27230 'kar' => 'audio/midi',
27231 'karbon' => 'application/vnd.kde.karbon',
27232 'kfo' => 'application/vnd.kde.kformula',
27233 'kia' => 'application/vnd.kidspiration',
27234 'kml' => 'application/vnd.google-earth.kml+xml',
27235 'kmz' => 'application/vnd.google-earth.kmz',
27236 'kne' => 'application/vnd.kinar',
27237 'knp' => 'application/vnd.kinar',
27238 'kon' => 'application/vnd.kde.kontour',
27239 'kpr' => 'application/vnd.kde.kpresenter',
27240 'kpt' => 'application/vnd.kde.kpresenter',
27241 'ksp' => 'application/vnd.kde.kspread',
27242 'ktr' => 'application/vnd.kahootz',
27243 'ktx' => 'image/ktx',
27244 'ktz' => 'application/vnd.kahootz',
27245 'kwd' => 'application/vnd.kde.kword',
27246 'kwt' => 'application/vnd.kde.kword',
27247 'lasxml' => 'application/vnd.las.las+xml',
27248 'latex' => 'application/x-latex',
27249 'lbd' => 'application/vnd.llamagraphics.life-balance.desktop',
27250 'lbe' => 'application/vnd.llamagraphics.life-balance.exchange+xml',
27251 'les' => 'application/vnd.hhe.lesson-player',
27252 'lha' => 'application/octet-stream',
27253 'link66' => 'application/vnd.route66.link66+xml',
27254 'list' => 'text/plain',
27255 'list3820' => 'application/vnd.ibm.modcap',
27256 'listafp' => 'application/vnd.ibm.modcap',
27257 'log' => 'text/plain',
27258 'lostxml' => 'application/lost+xml',
27259 'lrf' => 'application/octet-stream',
27260 'lrm' => 'application/vnd.ms-lrm',
27261 'ltf' => 'application/vnd.frogans.ltf',
27262 'lvp' => 'audio/vnd.lucent.voice',
27263 'lwp' => 'application/vnd.lotus-wordpro',
27264 'lzh' => 'application/octet-stream',
27265 'm13' => 'application/x-msmediaview',
27266 'm14' => 'application/x-msmediaview',
27267 'm1v' => 'video/mpeg',
27268 'm21' => 'application/mp21',
27269 'm2a' => 'audio/mpeg',
27270 'm2v' => 'video/mpeg',
27271 'm3a' => 'audio/mpeg',
27272 'm3u' => 'audio/x-mpegurl',
27273 'm3u8' => 'application/vnd.apple.mpegurl',
27274 'm4a' => 'audio/mp4',
27275 'm4u' => 'video/vnd.mpegurl',
27276 'm4v' => 'video/mp4',
27277 'ma' => 'application/mathematica',
27278 'mads' => 'application/mads+xml',
27279 'mag' => 'application/vnd.ecowin.chart',
27280 'maker' => 'application/vnd.framemaker',
27281 'man' => 'text/troff',
27282 'mathml' => 'application/mathml+xml',
27283 'mb' => 'application/mathematica',
27284 'mbk' => 'application/vnd.mobius.mbk',
27285 'mbox' => 'application/mbox',
27286 'mc1' => 'application/vnd.medcalcdata',
27287 'mcd' => 'application/vnd.mcd',
27288 'mcurl' => 'text/vnd.curl.mcurl',
27289 'mdb' => 'application/x-msaccess',
27290 'mdi' => 'image/vnd.ms-modi',
27291 'me' => 'text/troff',
27292 'mesh' => 'model/mesh',
27293 'meta4' => 'application/metalink4+xml',
27294 'mets' => 'application/mets+xml',
27295 'mfm' => 'application/vnd.mfmp',
27296 'mgp' => 'application/vnd.osgeo.mapguide.package',
27297 'mgz' => 'application/vnd.proteus.magazine',
27298 'mid' => 'audio/midi',
27299 'midi' => 'audio/midi',
27300 'mif' => 'application/vnd.mif',
27301 'mime' => 'message/rfc822',
27302 'mj2' => 'video/mj2',
27303 'mjp2' => 'video/mj2',
27304 'mlp' => 'application/vnd.dolby.mlp',
27305 'mmd' => 'application/vnd.chipnuts.karaoke-mmd',
27306 'mmf' => 'application/vnd.smaf',
27307 'mmr' => 'image/vnd.fujixerox.edmics-mmr',
27308 'mny' => 'application/x-msmoney',
27309 'mobi' => 'application/x-mobipocket-ebook',
27310 'mods' => 'application/mods+xml',
27311 'mov' => 'video/quicktime',
27312 'movie' => 'video/x-sgi-movie',
27313 'mp2' => 'audio/mpeg',
27314 'mp21' => 'application/mp21',
27315 'mp2a' => 'audio/mpeg',
27316 'mp3' => 'audio/mpeg',
27317 'mp4' => 'video/mp4',
27318 'mp4a' => 'audio/mp4',
27319 'mp4s' => 'application/mp4',
27320 'mp4v' => 'video/mp4',
27321 'mpc' => 'application/vnd.mophun.certificate',
27322 'mpe' => 'video/mpeg',
27323 'mpeg' => 'video/mpeg',
27324 'mpg' => 'video/mpeg',
27325 'mpg4' => 'video/mp4',
27326 'mpga' => 'audio/mpeg',
27327 'mpkg' => 'application/vnd.apple.installer+xml',
27328 'mpm' => 'application/vnd.blueice.multipass',
27329 'mpn' => 'application/vnd.mophun.application',
27330 'mpp' => 'application/vnd.ms-project',
27331 'mpt' => 'application/vnd.ms-project',
27332 'mpy' => 'application/vnd.ibm.minipay',
27333 'mqy' => 'application/vnd.mobius.mqy',
27334 'mrc' => 'application/marc',
27335 'mrcx' => 'application/marcxml+xml',
27336 'ms' => 'text/troff',
27337 'mscml' => 'application/mediaservercontrol+xml',
27338 'mseed' => 'application/vnd.fdsn.mseed',
27339 'mseq' => 'application/vnd.mseq',
27340 'msf' => 'application/vnd.epson.msf',
27341 'msh' => 'model/mesh',
27342 'msi' => 'application/x-msdownload',
27343 'msl' => 'application/vnd.mobius.msl',
27344 'msty' => 'application/vnd.muvee.style',
27345 'mts' => 'model/vnd.mts',
27346 'mus' => 'application/vnd.musician',
27347 'musicxml' => 'application/vnd.recordare.musicxml+xml',
27348 'mvb' => 'application/x-msmediaview',
27349 'mwf' => 'application/vnd.mfer',
27350 'mxf' => 'application/mxf',
27351 'mxl' => 'application/vnd.recordare.musicxml',
27352 'mxml' => 'application/xv+xml',
27353 'mxs' => 'application/vnd.triscape.mxs',
27354 'mxu' => 'video/vnd.mpegurl',
27355 'n-gage' => 'application/vnd.nokia.n-gage.symbian.install',
27356 'n3' => 'text/n3',
27357 'nb' => 'application/mathematica',
27358 'nbp' => 'application/vnd.wolfram.player',
27359 'nc' => 'application/x-netcdf',
27360 'ncx' => 'application/x-dtbncx+xml',
27361 'ngdat' => 'application/vnd.nokia.n-gage.data',
27362 'nlu' => 'application/vnd.neurolanguage.nlu',
27363 'nml' => 'application/vnd.enliven',
27364 'nnd' => 'application/vnd.noblenet-directory',
27365 'nns' => 'application/vnd.noblenet-sealer',
27366 'nnw' => 'application/vnd.noblenet-web',
27367 'npx' => 'image/vnd.net-fpx',
27368 'nsf' => 'application/vnd.lotus-notes',
27369 'oa2' => 'application/vnd.fujitsu.oasys2',
27370 'oa3' => 'application/vnd.fujitsu.oasys3',
27371 'oas' => 'application/vnd.fujitsu.oasys',
27372 'obd' => 'application/x-msbinder',
27373 'oda' => 'application/oda',
27374 'odb' => 'application/vnd.oasis.opendocument.database',
27375 'odc' => 'application/vnd.oasis.opendocument.chart',
27376 'odf' => 'application/vnd.oasis.opendocument.formula',
27377 'odft' => 'application/vnd.oasis.opendocument.formula-template',
27378 'odg' => 'application/vnd.oasis.opendocument.graphics',
27379 'odi' => 'application/vnd.oasis.opendocument.image',
27380 'odm' => 'application/vnd.oasis.opendocument.text-master',
27381 'odp' => 'application/vnd.oasis.opendocument.presentation',
27382 'ods' => 'application/vnd.oasis.opendocument.spreadsheet',
27383 'odt' => 'application/vnd.oasis.opendocument.text',
27384 'oga' => 'audio/ogg',
27385 'ogg' => 'audio/ogg',
27386 'ogv' => 'video/ogg',
27387 'ogx' => 'application/ogg',
27388 'onepkg' => 'application/onenote',
27389 'onetmp' => 'application/onenote',
27390 'onetoc' => 'application/onenote',
27391 'onetoc2' => 'application/onenote',
27392 'opf' => 'application/oebps-package+xml',
27393 'oprc' => 'application/vnd.palm',
27394 'org' => 'application/vnd.lotus-organizer',
27395 'osf' => 'application/vnd.yamaha.openscoreformat',
27396 'osfpvg' => 'application/vnd.yamaha.openscoreformat.osfpvg+xml',
27397 'otc' => 'application/vnd.oasis.opendocument.chart-template',
27398 'otf' => 'application/x-font-otf',
27399 'otg' => 'application/vnd.oasis.opendocument.graphics-template',
27400 'oth' => 'application/vnd.oasis.opendocument.text-web',
27401 'oti' => 'application/vnd.oasis.opendocument.image-template',
27402 'otp' => 'application/vnd.oasis.opendocument.presentation-template',
27403 'ots' => 'application/vnd.oasis.opendocument.spreadsheet-template',
27404 'ott' => 'application/vnd.oasis.opendocument.text-template',
27405 'oxt' => 'application/vnd.openofficeorg.extension',
27406 'p' => 'text/x-pascal',
27407 'p10' => 'application/pkcs10',
27408 'p12' => 'application/x-pkcs12',
27409 'p7b' => 'application/x-pkcs7-certificates',
27410 'p7c' => 'application/pkcs7-mime',
27411 'p7m' => 'application/pkcs7-mime',
27412 'p7r' => 'application/x-pkcs7-certreqresp',
27413 'p7s' => 'application/pkcs7-signature',
27414 'p8' => 'application/pkcs8',
27415 'pas' => 'text/x-pascal',
27416 'paw' => 'application/vnd.pawaafile',
27417 'pbd' => 'application/vnd.powerbuilder6',
27418 'pbm' => 'image/x-portable-bitmap',
27419 'pcf' => 'application/x-font-pcf',
27420 'pcl' => 'application/vnd.hp-pcl',
27421 'pclxl' => 'application/vnd.hp-pclxl',
27422 'pct' => 'image/x-pict',
27423 'pcurl' => 'application/vnd.curl.pcurl',
27424 'pcx' => 'image/x-pcx',
27425 'pdb' => 'application/vnd.palm',
27426 'pdf' => 'application/pdf',
27427 'pfa' => 'application/x-font-type1',
27428 'pfb' => 'application/x-font-type1',
27429 'pfm' => 'application/x-font-type1',
27430 'pfr' => 'application/font-tdpfr',
27431 'pfx' => 'application/x-pkcs12',
27432 'pgm' => 'image/x-portable-graymap',
27433 'pgn' => 'application/x-chess-pgn',
27434 'pgp' => 'application/pgp-encrypted',
27435 'php' => 'text/x-php',
27436 'phps' => 'application/x-httpd-phps',
27437 'pic' => 'image/x-pict',
27438 'pkg' => 'application/octet-stream',
27439 'pki' => 'application/pkixcmp',
27440 'pkipath' => 'application/pkix-pkipath',
27441 'plb' => 'application/vnd.3gpp.pic-bw-large',
27442 'plc' => 'application/vnd.mobius.plc',
27443 'plf' => 'application/vnd.pocketlearn',
27444 'pls' => 'application/pls+xml',
27445 'pml' => 'application/vnd.ctc-posml',
27446 'png' => 'image/png',
27447 'pnm' => 'image/x-portable-anymap',
27448 'portpkg' => 'application/vnd.macports.portpkg',
27449 'pot' => 'application/vnd.ms-powerpoint',
27450 'potm' => 'application/vnd.ms-powerpoint.template.macroenabled.12',
27451 'potx' => 'application/vnd.openxmlformats-officedocument.presentationml.template',
27452 'ppam' => 'application/vnd.ms-powerpoint.addin.macroenabled.12',
27453 'ppd' => 'application/vnd.cups-ppd',
27454 'ppm' => 'image/x-portable-pixmap',
27455 'pps' => 'application/vnd.ms-powerpoint',
27456 'ppsm' => 'application/vnd.ms-powerpoint.slideshow.macroenabled.12',
27457 'ppsx' => 'application/vnd.openxmlformats-officedocument.presentationml.slideshow',
27458 'ppt' => 'application/vnd.ms-powerpoint',
27459 'pptm' => 'application/vnd.ms-powerpoint.presentation.macroenabled.12',
27460 'pptx' => 'application/vnd.openxmlformats-officedocument.presentationml.presentation',
27461 'pqa' => 'application/vnd.palm',
27462 'prc' => 'application/x-mobipocket-ebook',
27463 'pre' => 'application/vnd.lotus-freelance',
27464 'prf' => 'application/pics-rules',
27465 'ps' => 'application/postscript',
27466 'psb' => 'application/vnd.3gpp.pic-bw-small',
27467 'psd' => 'image/vnd.adobe.photoshop',
27468 'psf' => 'application/x-font-linux-psf',
27469 'pskcxml' => 'application/pskc+xml',
27470 'ptid' => 'application/vnd.pvi.ptid1',
27471 'pub' => 'application/x-mspublisher',
27472 'pvb' => 'application/vnd.3gpp.pic-bw-var',
27473 'pwn' => 'application/vnd.3m.post-it-notes',
27474 'pya' => 'audio/vnd.ms-playready.media.pya',
27475 'pyv' => 'video/vnd.ms-playready.media.pyv',
27476 'qam' => 'application/vnd.epson.quickanime',
27477 'qbo' => 'application/vnd.intu.qbo',
27478 'qfx' => 'application/vnd.intu.qfx',
27479 'qps' => 'application/vnd.publishare-delta-tree',
27480 'qt' => 'video/quicktime',
27481 'qwd' => 'application/vnd.quark.quarkxpress',
27482 'qwt' => 'application/vnd.quark.quarkxpress',
27483 'qxb' => 'application/vnd.quark.quarkxpress',
27484 'qxd' => 'application/vnd.quark.quarkxpress',
27485 'qxl' => 'application/vnd.quark.quarkxpress',
27486 'qxt' => 'application/vnd.quark.quarkxpress',
27487 'ra' => 'audio/x-pn-realaudio',
27488 'ram' => 'audio/x-pn-realaudio',
27489 'rar' => 'application/x-rar-compressed',
27490 'ras' => 'image/x-cmu-raster',
27491 'rb' => 'text/plain',
27492 'rcprofile' => 'application/vnd.ipunplugged.rcprofile',
27493 'rdf' => 'application/rdf+xml',
27494 'rdz' => 'application/vnd.data-vision.rdz',
27495 'rep' => 'application/vnd.businessobjects',
27496 'res' => 'application/x-dtbresource+xml',
27497 'resx' => 'text/xml',
27498 'rgb' => 'image/x-rgb',
27499 'rif' => 'application/reginfo+xml',
27500 'rip' => 'audio/vnd.rip',
27501 'rl' => 'application/resource-lists+xml',
27502 'rlc' => 'image/vnd.fujixerox.edmics-rlc',
27503 'rld' => 'application/resource-lists-diff+xml',
27504 'rm' => 'application/vnd.rn-realmedia',
27505 'rmi' => 'audio/midi',
27506 'rmp' => 'audio/x-pn-realaudio-plugin',
27507 'rms' => 'application/vnd.jcp.javame.midlet-rms',
27508 'rnc' => 'application/relax-ng-compact-syntax',
27509 'roff' => 'text/troff',
27510 'rp9' => 'application/vnd.cloanto.rp9',
27511 'rpss' => 'application/vnd.nokia.radio-presets',
27512 'rpst' => 'application/vnd.nokia.radio-preset',
27513 'rq' => 'application/sparql-query',
27514 'rs' => 'application/rls-services+xml',
27515 'rsd' => 'application/rsd+xml',
27516 'rss' => 'application/rss+xml',
27517 'rtf' => 'application/rtf',
27518 'rtx' => 'text/richtext',
27519 's' => 'text/x-asm',
27520 'saf' => 'application/vnd.yamaha.smaf-audio',
27521 'sbml' => 'application/sbml+xml',
27522 'sc' => 'application/vnd.ibm.secure-container',
27523 'scd' => 'application/x-msschedule',
27524 'scm' => 'application/vnd.lotus-screencam',
27525 'scq' => 'application/scvp-cv-request',
27526 'scs' => 'application/scvp-cv-response',
27527 'scurl' => 'text/vnd.curl.scurl',
27528 'sda' => 'application/vnd.stardivision.draw',
27529 'sdc' => 'application/vnd.stardivision.calc',
27530 'sdd' => 'application/vnd.stardivision.impress',
27531 'sdkd' => 'application/vnd.solent.sdkm+xml',
27532 'sdkm' => 'application/vnd.solent.sdkm+xml',
27533 'sdp' => 'application/sdp',
27534 'sdw' => 'application/vnd.stardivision.writer',
27535 'see' => 'application/vnd.seemail',
27536 'seed' => 'application/vnd.fdsn.seed',
27537 'sema' => 'application/vnd.sema',
27538 'semd' => 'application/vnd.semd',
27539 'semf' => 'application/vnd.semf',
27540 'ser' => 'application/java-serialized-object',
27541 'setpay' => 'application/set-payment-initiation',
27542 'setreg' => 'application/set-registration-initiation',
27543 'sfd-hdstx' => 'application/vnd.hydrostatix.sof-data',
27544 'sfs' => 'application/vnd.spotfire.sfs',
27545 'sgl' => 'application/vnd.stardivision.writer-global',
27546 'sgm' => 'text/sgml',
27547 'sgml' => 'text/sgml',
27548 'sh' => 'application/x-sh',
27549 'shar' => 'application/x-shar',
27550 'shf' => 'application/shf+xml',
27551 'sig' => 'application/pgp-signature',
27552 'silo' => 'model/mesh',
27553 'sis' => 'application/vnd.symbian.install',
27554 'sisx' => 'application/vnd.symbian.install',
27555 'sit' => 'application/x-stuffit',
27556 'sitx' => 'application/x-stuffitx',
27557 'skd' => 'application/vnd.koan',
27558 'skm' => 'application/vnd.koan',
27559 'skp' => 'application/vnd.koan',
27560 'skt' => 'application/vnd.koan',
27561 'sldm' => 'application/vnd.ms-powerpoint.slide.macroenabled.12',
27562 'sldx' => 'application/vnd.openxmlformats-officedocument.presentationml.slide',
27563 'slt' => 'application/vnd.epson.salt',
27564 'sm' => 'application/vnd.stepmania.stepchart',
27565 'smf' => 'application/vnd.stardivision.math',
27566 'smi' => 'application/smil+xml',
27567 'smil' => 'application/smil+xml',
27568 'snd' => 'audio/basic',
27569 'snf' => 'application/x-font-snf',
27570 'so' => 'application/octet-stream',
27571 'spc' => 'application/x-pkcs7-certificates',
27572 'spf' => 'application/vnd.yamaha.smaf-phrase',
27573 'spl' => 'application/x-futuresplash',
27574 'spot' => 'text/vnd.in3d.spot',
27575 'spp' => 'application/scvp-vp-response',
27576 'spq' => 'application/scvp-vp-request',
27577 'spx' => 'audio/ogg',
27578 'src' => 'application/x-wais-source',
27579 'sru' => 'application/sru+xml',
27580 'srx' => 'application/sparql-results+xml',
27581 'sse' => 'application/vnd.kodak-descriptor',
27582 'ssf' => 'application/vnd.epson.ssf',
27583 'ssml' => 'application/ssml+xml',
27584 'st' => 'application/vnd.sailingtracker.track',
27585 'stc' => 'application/vnd.sun.xml.calc.template',
27586 'std' => 'application/vnd.sun.xml.draw.template',
27587 'stf' => 'application/vnd.wt.stf',
27588 'sti' => 'application/vnd.sun.xml.impress.template',
27589 'stk' => 'application/hyperstudio',
27590 'stl' => 'application/vnd.ms-pki.stl',
27591 'str' => 'application/vnd.pg.format',
27592 'stw' => 'application/vnd.sun.xml.writer.template',
27593 'sub' => 'image/vnd.dvb.subtitle',
27594 'sus' => 'application/vnd.sus-calendar',
27595 'susp' => 'application/vnd.sus-calendar',
27596 'sv4cpio' => 'application/x-sv4cpio',
27597 'sv4crc' => 'application/x-sv4crc',
27598 'svc' => 'application/vnd.dvb.service',
27599 'svd' => 'application/vnd.svd',
27600 'svg' => 'image/svg+xml',
27601 'svgz' => 'image/svg+xml',
27602 'swa' => 'application/x-director',
27603 'swf' => 'application/x-shockwave-flash',
27604 'swi' => 'application/vnd.aristanetworks.swi',
27605 'sxc' => 'application/vnd.sun.xml.calc',
27606 'sxd' => 'application/vnd.sun.xml.draw',
27607 'sxg' => 'application/vnd.sun.xml.writer.global',
27608 'sxi' => 'application/vnd.sun.xml.impress',
27609 'sxm' => 'application/vnd.sun.xml.math',
27610 'sxw' => 'application/vnd.sun.xml.writer',
27611 't' => 'text/troff',
27612 'tao' => 'application/vnd.tao.intent-module-archive',
27613 'tar' => 'application/x-tar',
27614 'tcap' => 'application/vnd.3gpp2.tcap',
27615 'tcl' => 'application/x-tcl',
27616 'teacher' => 'application/vnd.smart.teacher',
27617 'tei' => 'application/tei+xml',
27618 'teicorpus' => 'application/tei+xml',
27619 'tex' => 'application/x-tex',
27620 'texi' => 'application/x-texinfo',
27621 'texinfo' => 'application/x-texinfo',
27622 'text' => 'text/plain',
27623 'tfi' => 'application/thraud+xml',
27624 'tfm' => 'application/x-tex-tfm',
27625 'thmx' => 'application/vnd.ms-officetheme',
27626 'tif' => 'image/tiff',
27627 'tiff' => 'image/tiff',
27628 'tmo' => 'application/vnd.tmobile-livetv',
27629 'torrent' => 'application/x-bittorrent',
27630 'tpl' => 'application/vnd.groove-tool-template',
27631 'tpt' => 'application/vnd.trid.tpt',
27632 'tr' => 'text/troff',
27633 'tra' => 'application/vnd.trueapp',
27634 'trm' => 'application/x-msterminal',
27635 'tsd' => 'application/timestamped-data',
27636 'tsv' => 'text/tab-separated-values',
27637 'ttc' => 'application/x-font-ttf',
27638 'ttf' => 'application/x-font-ttf',
27639 'ttl' => 'text/turtle',
27640 'twd' => 'application/vnd.simtech-mindmapper',
27641 'twds' => 'application/vnd.simtech-mindmapper',
27642 'txd' => 'application/vnd.genomatix.tuxedo',
27643 'txf' => 'application/vnd.mobius.txf',
27644 'txt' => 'text/plain',
27645 'u32' => 'application/x-authorware-bin',
27646 'udeb' => 'application/x-debian-package',
27647 'ufd' => 'application/vnd.ufdl',
27648 'ufdl' => 'application/vnd.ufdl',
27649 'umj' => 'application/vnd.umajin',
27650 'unityweb' => 'application/vnd.unity',
27651 'uoml' => 'application/vnd.uoml+xml',
27652 'uri' => 'text/uri-list',
27653 'uris' => 'text/uri-list',
27654 'urls' => 'text/uri-list',
27655 'ustar' => 'application/x-ustar',
27656 'utz' => 'application/vnd.uiq.theme',
27657 'uu' => 'text/x-uuencode',
27658 'uva' => 'audio/vnd.dece.audio',
27659 'uvd' => 'application/vnd.dece.data',
27660 'uvf' => 'application/vnd.dece.data',
27661 'uvg' => 'image/vnd.dece.graphic',
27662 'uvh' => 'video/vnd.dece.hd',
27663 'uvi' => 'image/vnd.dece.graphic',
27664 'uvm' => 'video/vnd.dece.mobile',
27665 'uvp' => 'video/vnd.dece.pd',
27666 'uvs' => 'video/vnd.dece.sd',
27667 'uvt' => 'application/vnd.dece.ttml+xml',
27668 'uvu' => 'video/vnd.uvvu.mp4',
27669 'uvv' => 'video/vnd.dece.video',
27670 'uvva' => 'audio/vnd.dece.audio',
27671 'uvvd' => 'application/vnd.dece.data',
27672 'uvvf' => 'application/vnd.dece.data',
27673 'uvvg' => 'image/vnd.dece.graphic',
27674 'uvvh' => 'video/vnd.dece.hd',
27675 'uvvi' => 'image/vnd.dece.graphic',
27676 'uvvm' => 'video/vnd.dece.mobile',
27677 'uvvp' => 'video/vnd.dece.pd',
27678 'uvvs' => 'video/vnd.dece.sd',
27679 'uvvt' => 'application/vnd.dece.ttml+xml',
27680 'uvvu' => 'video/vnd.uvvu.mp4',
27681 'uvvv' => 'video/vnd.dece.video',
27682 'uvvx' => 'application/vnd.dece.unspecified',
27683 'uvx' => 'application/vnd.dece.unspecified',
27684 'vcd' => 'application/x-cdlink',
27685 'vcf' => 'text/x-vcard',
27686 'vcg' => 'application/vnd.groove-vcard',
27687 'vcs' => 'text/x-vcalendar',
27688 'vcx' => 'application/vnd.vcx',
27689 'vis' => 'application/vnd.visionary',
27690 'viv' => 'video/vnd.vivo',
27691 'vor' => 'application/vnd.stardivision.writer',
27692 'vox' => 'application/x-authorware-bin',
27693 'vrml' => 'model/vrml',
27694 'vsd' => 'application/vnd.visio',
27695 'vsf' => 'application/vnd.vsf',
27696 'vss' => 'application/vnd.visio',
27697 'vst' => 'application/vnd.visio',
27698 'vsw' => 'application/vnd.visio',
27699 'vtu' => 'model/vnd.vtu',
27700 'vxml' => 'application/voicexml+xml',
27701 'w3d' => 'application/x-director',
27702 'wad' => 'application/x-doom',
27703 'wav' => 'audio/x-wav',
27704 'wax' => 'audio/x-ms-wax',
27705 'wbmp' => 'image/vnd.wap.wbmp',
27706 'wbs' => 'application/vnd.criticaltools.wbs+xml',
27707 'wbxml' => 'application/vnd.wap.wbxml',
27708 'wcm' => 'application/vnd.ms-works',
27709 'wdb' => 'application/vnd.ms-works',
27710 'weba' => 'audio/webm',
27711 'webm' => 'video/webm',
27712 'webp' => 'image/webp',
27713 'wg' => 'application/vnd.pmi.widget',
27714 'wgt' => 'application/widget',
27715 'wks' => 'application/vnd.ms-works',
27716 'wm' => 'video/x-ms-wm',
27717 'wma' => 'audio/x-ms-wma',
27718 'wmd' => 'application/x-ms-wmd',
27719 'wmf' => 'application/x-msmetafile',
27720 'wml' => 'text/vnd.wap.wml',
27721 'wmlc' => 'application/vnd.wap.wmlc',
27722 'wmls' => 'text/vnd.wap.wmlscript',
27723 'wmlsc' => 'application/vnd.wap.wmlscriptc',
27724 'wmv' => 'video/x-ms-wmv',
27725 'wmx' => 'video/x-ms-wmx',
27726 'wmz' => 'application/x-ms-wmz',
27727 'woff' => 'application/x-font-woff',
27728 'wpd' => 'application/vnd.wordperfect',
27729 'wpl' => 'application/vnd.ms-wpl',
27730 'wps' => 'application/vnd.ms-works',
27731 'wqd' => 'application/vnd.wqd',
27732 'wri' => 'application/x-mswrite',
27733 'wrl' => 'model/vrml',
27734 'wsdl' => 'application/wsdl+xml',
27735 'wspolicy' => 'application/wspolicy+xml',
27736 'wtb' => 'application/vnd.webturbo',
27737 'wvx' => 'video/x-ms-wvx',
27738 'x32' => 'application/x-authorware-bin',
27739 'x3d' => 'application/vnd.hzn-3d-crossword',
27740 'xap' => 'application/x-silverlight-app',
27741 'xar' => 'application/vnd.xara',
27742 'xbap' => 'application/x-ms-xbap',
27743 'xbd' => 'application/vnd.fujixerox.docuworks.binder',
27744 'xbm' => 'image/x-xbitmap',
27745 'xdf' => 'application/xcap-diff+xml',
27746 'xdm' => 'application/vnd.syncml.dm+xml',
27747 'xdp' => 'application/vnd.adobe.xdp+xml',
27748 'xdssc' => 'application/dssc+xml',
27749 'xdw' => 'application/vnd.fujixerox.docuworks',
27750 'xenc' => 'application/xenc+xml',
27751 'xer' => 'application/patch-ops-error+xml',
27752 'xfdf' => 'application/vnd.adobe.xfdf',
27753 'xfdl' => 'application/vnd.xfdl',
27754 'xht' => 'application/xhtml+xml',
27755 'xhtml' => 'application/xhtml+xml',
27756 'xhvml' => 'application/xv+xml',
27757 'xif' => 'image/vnd.xiff',
27758 'xla' => 'application/vnd.ms-excel',
27759 'xlam' => 'application/vnd.ms-excel.addin.macroenabled.12',
27760 'xlc' => 'application/vnd.ms-excel',
27761 'xlm' => 'application/vnd.ms-excel',
27762 'xls' => 'application/vnd.ms-excel',
27763 'xlsb' => 'application/vnd.ms-excel.sheet.binary.macroenabled.12',
27764 'xlsm' => 'application/vnd.ms-excel.sheet.macroenabled.12',
27765 'xlsx' => 'application/vnd.openxmlformats-officedocument.spreadsheetml.sheet',
27766 'xlt' => 'application/vnd.ms-excel',
27767 'xltm' => 'application/vnd.ms-excel.template.macroenabled.12',
27768 'xltx' => 'application/vnd.openxmlformats-officedocument.spreadsheetml.template',
27769 'xlw' => 'application/vnd.ms-excel',
27770 'xml' => 'application/xml',
27771 'xo' => 'application/vnd.olpc-sugar',
27772 'xop' => 'application/xop+xml',
27773 'xpi' => 'application/x-xpinstall',
27774 'xpm' => 'image/x-xpixmap',
27775 'xpr' => 'application/vnd.is-xpr',
27776 'xps' => 'application/vnd.ms-xpsdocument',
27777 'xpw' => 'application/vnd.intercon.formnet',
27778 'xpx' => 'application/vnd.intercon.formnet',
27779 'xsl' => 'application/xml',
27780 'xslt' => 'application/xslt+xml',
27781 'xsm' => 'application/vnd.syncml+xml',
27782 'xspf' => 'application/xspf+xml',
27783 'xul' => 'application/vnd.mozilla.xul+xml',
27784 'xvm' => 'application/xv+xml',
27785 'xvml' => 'application/xv+xml',
27786 'xwd' => 'image/x-xwindowdump',
27787 'xyz' => 'chemical/x-xyz',
27788 'yaml' => 'text/yaml',
27789 'yang' => 'application/yang',
27790 'yin' => 'application/yin+xml',
27791 'yml' => 'text/yaml',
27792 'zaz' => 'application/vnd.zzazz.deck+xml',
27793 'zip' => 'application/zip',
27794 'zir' => 'application/vnd.zul',
27795 'zirz' => 'application/vnd.zul',
27796 'zmm' => 'application/vnd.handheld-entertainment+xml'
27797 );
27798
27799 /**
27800 * Get a singleton instance of the class
27801 *
27802 * @return self
27803 * @codeCoverageIgnore
27804 */
27805 public static function getInstance()
27806 {
27807 if (!self::$instance) {
27808 self::$instance = new self();
27809 }
27810
27811 return self::$instance;
27812 }
27813
27814 /**
27815 * Get a mimetype value from a file extension
27816 *
27817 * @param string $extension File extension
27818 *
27819 * @return string|null
27820 *
27821 */
27822 public function fromExtension($extension)
27823 {
27824 $extension = strtolower($extension);
27825
27826 return isset($this->mimetypes[$extension]) ? $this->mimetypes[$extension] : null;
27827 }
27828
27829 /**
27830 * Get a mimetype from a filename
27831 *
27832 * @param string $filename Filename to generate a mimetype from
27833 *
27834 * @return string|null
27835 */
27836 public function fromFilename($filename)
27837 {
27838 return $this->fromExtension(pathinfo($filename, PATHINFO_EXTENSION));
27839 }
27840 }
27841 <?php
27842
27843 namespace Guzzle\Http;
27844
27845 /**
27846 * EntityBody decorator used to return only a subset of an entity body
27847 */
27848 class ReadLimitEntityBody extends AbstractEntityBodyDecorator
27849 {
27850 /** @var int Limit the number of bytes that can be read */
27851 protected $limit;
27852
27853 /** @var int Offset to start reading from */
27854 protected $offset;
27855
27856 /**
27857 * @param EntityBodyInterface $body Body to wrap
27858 * @param int $limit Total number of bytes to allow to be read from the stream
27859 * @param int $offset Position to seek to before reading (only works on seekable streams)
27860 */
27861 public function __construct(EntityBodyInterface $body, $limit, $offset = 0)
27862 {
27863 parent::__construct($body);
27864 $this->setLimit($limit)->setOffset($offset);
27865 }
27866
27867 /**
27868 * Returns only a subset of the decorated entity body when cast as a string
27869 * {@inheritdoc}
27870 */
27871 public function __toString()
27872 {
27873 return substr((string) $this->body, $this->offset, $this->limit) ?: '';
27874 }
27875
27876 public function isConsumed()
27877 {
27878 return (($this->offset + $this->limit) - $this->body->ftell()) <= 0;
27879 }
27880
27881 /**
27882 * Returns the Content-Length of the limited subset of data
27883 * {@inheritdoc}
27884 */
27885 public function getContentLength()
27886 {
27887 $length = $this->body->getContentLength();
27888
27889 return $length === false
27890 ? $this->limit
27891 : min($this->limit, min($length, $this->offset + $this->limit) - $this->offset);
27892 }
27893
27894 /**
27895 * Allow for a bounded seek on the read limited entity body
27896 * {@inheritdoc}
27897 */
27898 public function seek($offset, $whence = SEEK_SET)
27899 {
27900 return $whence === SEEK_SET
27901 ? $this->body->seek(max($this->offset, min($this->offset + $this->limit, $offset)))
27902 : false;
27903 }
27904
27905 /**
27906 * Set the offset to start limiting from
27907 *
27908 * @param int $offset Offset to seek to and begin byte limiting from
27909 *
27910 * @return self
27911 */
27912 public function setOffset($offset)
27913 {
27914 $this->body->seek($offset);
27915 $this->offset = $offset;
27916
27917 return $this;
27918 }
27919
27920 /**
27921 * Set the limit of bytes that the decorator allows to be read from the stream
27922 *
27923 * @param int $limit Total number of bytes to allow to be read from the stream
27924 *
27925 * @return self
27926 */
27927 public function setLimit($limit)
27928 {
27929 $this->limit = $limit;
27930
27931 return $this;
27932 }
27933
27934 public function read($length)
27935 {
27936 // Check if the current position is less than the total allowed bytes + original offset
27937 $remaining = ($this->offset + $this->limit) - $this->body->ftell();
27938 if ($remaining > 0) {
27939 // Only return the amount of requested data, ensuring that the byte limit is not exceeded
27940 return $this->body->read(min($remaining, $length));
27941 } else {
27942 return false;
27943 }
27944 }
27945 }
27946 <?php
27947
27948 namespace Guzzle\Http;
27949
27950 use Guzzle\Common\Collection;
27951 use Guzzle\Http\QueryAggregator\DuplicateAggregator;
27952 use Guzzle\Http\QueryAggregator\QueryAggregatorInterface;
27953 use Guzzle\Http\QueryAggregator\PhpAggregator;
27954
27955 /**
27956 * Query string object to handle managing query string parameters and aggregating those parameters together as a string.
27957 */
27958 class QueryString extends Collection
27959 {
27960 /** @var string Used to URL encode with rawurlencode */
27961 const RFC_3986 = 'RFC 3986';
27962
27963 /** @var string Used to encode with urlencode */
27964 const FORM_URLENCODED = 'application/x-www-form-urlencoded';
27965
27966 /** @var string Constant used to create blank query string values (e.g. ?foo) */
27967 const BLANK = "_guzzle_blank_";
27968
27969 /** @var string The query string field separator (e.g. '&') */
27970 protected $fieldSeparator = '&';
27971
27972 /** @var string The query string value separator (e.g. '=') */
27973 protected $valueSeparator = '=';
27974
27975 /** @var bool URL encode fields and values */
27976 protected $urlEncode = 'RFC 3986';
27977
27978 /** @var QueryAggregatorInterface */
27979 protected $aggregator;
27980
27981 /** @var array Cached PHP aggregator */
27982 private static $defaultAggregator = null;
27983
27984 /**
27985 * Parse a query string into a QueryString object
27986 *
27987 * @param string $query Query string to parse
27988 *
27989 * @return self
27990 */
27991 public static function fromString($query)
27992 {
27993 $q = new static();
27994 if ($query === '') {
27995 return $q;
27996 }
27997
27998 $foundDuplicates = $foundPhpStyle = false;
27999
28000 foreach (explode('&', $query) as $kvp) {
28001 $parts = explode('=', $kvp, 2);
28002 $key = rawurldecode($parts[0]);
28003 if ($paramIsPhpStyleArray = substr($key, -2) == '[]') {
28004 $foundPhpStyle = true;
28005 $key = substr($key, 0, -2);
28006 }
28007 if (isset($parts[1])) {
28008 $value = rawurldecode(str_replace('+', '%20', $parts[1]));
28009 if (isset($q[$key])) {
28010 $q->add($key, $value);
28011 $foundDuplicates = true;
28012 } elseif ($paramIsPhpStyleArray) {
28013 $q[$key] = array($value);
28014 } else {
28015 $q[$key] = $value;
28016 }
28017 } else {
28018 $q->add($key, null);
28019 }
28020 }
28021
28022 // Use the duplicate aggregator if duplicates were found and not using PHP style arrays
28023 if ($foundDuplicates && !$foundPhpStyle) {
28024 $q->setAggregator(new DuplicateAggregator());
28025 }
28026
28027 return $q;
28028 }
28029
28030 /**
28031 * Convert the query string parameters to a query string string
28032 *
28033 * @return string
28034 */
28035 public function __toString()
28036 {
28037 if (!$this->data) {
28038 return '';
28039 }
28040
28041 $queryString = '';
28042
28043 foreach ($this->prepareData($this->data) as $name => $value) {
28044 foreach ((array) $value as $v) {
28045 if ($queryString) {
28046 $queryString .= $this->fieldSeparator;
28047 }
28048 $queryString .= $name;
28049 if ($v !== self::BLANK) {
28050 $queryString .= $this->valueSeparator . $v;
28051 }
28052 }
28053 }
28054
28055 return $queryString;
28056 }
28057
28058 /**
28059 * Get the query string field separator
28060 *
28061 * @return string
28062 */
28063 public function getFieldSeparator()
28064 {
28065 return $this->fieldSeparator;
28066 }
28067
28068 /**
28069 * Get the query string value separator
28070 *
28071 * @return string
28072 */
28073 public function getValueSeparator()
28074 {
28075 return $this->valueSeparator;
28076 }
28077
28078 /**
28079 * Returns the type of URL encoding used by the query string
28080 *
28081 * One of: false, "RFC 3986", or "application/x-www-form-urlencoded"
28082 *
28083 * @return bool|string
28084 */
28085 public function getUrlEncoding()
28086 {
28087 return $this->urlEncode;
28088 }
28089
28090 /**
28091 * Returns true or false if using URL encoding
28092 *
28093 * @return bool
28094 */
28095 public function isUrlEncoding()
28096 {
28097 return $this->urlEncode !== false;
28098 }
28099
28100 /**
28101 * Provide a function for combining multi-valued query string parameters into a single or multiple fields
28102 *
28103 * @param null|QueryAggregatorInterface $aggregator Pass in a QueryAggregatorInterface object to handle converting
28104 * deeply nested query string variables into a flattened array.
28105 * Pass null to use the default PHP style aggregator. For legacy
28106 * reasons, this function accepts a callable that must accepts a
28107 * $key, $value, and query object.
28108 * @return self
28109 * @see \Guzzle\Http\QueryString::aggregateUsingComma()
28110 */
28111 public function setAggregator(QueryAggregatorInterface $aggregator = null)
28112 {
28113 // Use the default aggregator if none was set
28114 if (!$aggregator) {
28115 if (!self::$defaultAggregator) {
28116 self::$defaultAggregator = new PhpAggregator();
28117 }
28118 $aggregator = self::$defaultAggregator;
28119 }
28120
28121 $this->aggregator = $aggregator;
28122
28123 return $this;
28124 }
28125
28126 /**
28127 * Set whether or not field names and values should be rawurlencoded
28128 *
28129 * @param bool|string $encode Set to TRUE to use RFC 3986 encoding (rawurlencode), false to disable encoding, or
28130 * form_urlencoding to use application/x-www-form-urlencoded encoding (urlencode)
28131 * @return self
28132 */
28133 public function useUrlEncoding($encode)
28134 {
28135 $this->urlEncode = ($encode === true) ? self::RFC_3986 : $encode;
28136
28137 return $this;
28138 }
28139
28140 /**
28141 * Set the query string separator
28142 *
28143 * @param string $separator The query string separator that will separate fields
28144 *
28145 * @return self
28146 */
28147 public function setFieldSeparator($separator)
28148 {
28149 $this->fieldSeparator = $separator;
28150
28151 return $this;
28152 }
28153
28154 /**
28155 * Set the query string value separator
28156 *
28157 * @param string $separator The query string separator that will separate values from fields
28158 *
28159 * @return self
28160 */
28161 public function setValueSeparator($separator)
28162 {
28163 $this->valueSeparator = $separator;
28164
28165 return $this;
28166 }
28167
28168 /**
28169 * Returns an array of url encoded field names and values
28170 *
28171 * @return array
28172 */
28173 public function urlEncode()
28174 {
28175 return $this->prepareData($this->data);
28176 }
28177
28178 /**
28179 * URL encodes a value based on the url encoding type of the query string object
28180 *
28181 * @param string $value Value to encode
28182 *
28183 * @return string
28184 */
28185 public function encodeValue($value)
28186 {
28187 if ($this->urlEncode == self::RFC_3986) {
28188 return rawurlencode($value);
28189 } elseif ($this->urlEncode == self::FORM_URLENCODED) {
28190 return urlencode($value);
28191 } else {
28192 return (string) $value;
28193 }
28194 }
28195
28196 /**
28197 * Url encode parameter data and convert nested query strings into a flattened hash.
28198 *
28199 * @param array $data The data to encode
28200 *
28201 * @return array Returns an array of encoded values and keys
28202 */
28203 protected function prepareData(array $data)
28204 {
28205 // If no aggregator is present then set the default
28206 if (!$this->aggregator) {
28207 $this->setAggregator(null);
28208 }
28209
28210 $temp = array();
28211 foreach ($data as $key => $value) {
28212 if (is_array($value)) {
28213 $temp = array_merge($temp, $this->aggregator->aggregate($key, $value, $this));
28214 } else {
28215 $temp[$this->encodeValue($key)] = $this->encodeValue($value);
28216 }
28217 }
28218
28219 return $temp;
28220 }
28221 }
28222 <?php
28223
28224 namespace Guzzle\Http;
28225
28226 use Guzzle\Common\Event;
28227 use Guzzle\Http\Exception\BadResponseException;
28228 use Guzzle\Http\Url;
28229 use Guzzle\Http\Message\Response;
28230 use Guzzle\Http\Message\RequestInterface;
28231 use Guzzle\Http\Message\RequestFactory;
28232 use Guzzle\Http\Message\EntityEnclosingRequestInterface;
28233 use Guzzle\Http\Exception\TooManyRedirectsException;
28234 use Guzzle\Http\Exception\CouldNotRewindStreamException;
28235 use Symfony\Component\EventDispatcher\EventSubscriberInterface;
28236
28237 /**
28238 * Plugin to implement HTTP redirects. Can redirect like a web browser or using strict RFC 2616 compliance
28239 */
28240 class RedirectPlugin implements EventSubscriberInterface
28241 {
28242 const REDIRECT_COUNT = 'redirect.count';
28243 const MAX_REDIRECTS = 'redirect.max';
28244 const STRICT_REDIRECTS = 'redirect.strict';
28245 const PARENT_REQUEST = 'redirect.parent_request';
28246 const DISABLE = 'redirect.disable';
28247
28248 /**
28249 * @var int Default number of redirects allowed when no setting is supplied by a request
28250 */
28251 protected $defaultMaxRedirects = 5;
28252
28253 public static function getSubscribedEvents()
28254 {
28255 return array(
28256 'request.sent' => array('onRequestSent', 100),
28257 'request.clone' => 'cleanupRequest',
28258 'request.before_send' => 'cleanupRequest'
28259 );
28260 }
28261
28262 /**
28263 * Clean up the parameters of a request when it is cloned
28264 *
28265 * @param Event $event Event emitted
28266 */
28267 public function cleanupRequest(Event $event)
28268 {
28269 $params = $event['request']->getParams();
28270 unset($params[self::REDIRECT_COUNT]);
28271 unset($params[self::PARENT_REQUEST]);
28272 }
28273
28274 /**
28275 * Called when a request receives a redirect response
28276 *
28277 * @param Event $event Event emitted
28278 */
28279 public function onRequestSent(Event $event)
28280 {
28281 $response = $event['response'];
28282 $request = $event['request'];
28283
28284 // Only act on redirect requests with Location headers
28285 if (!$response || $request->getParams()->get(self::DISABLE)) {
28286 return;
28287 }
28288
28289 // Trace the original request based on parameter history
28290 $original = $this->getOriginalRequest($request);
28291
28292 // Terminating condition to set the effective repsonse on the original request
28293 if (!$response->isRedirect() || !$response->hasHeader('Location')) {
28294 if ($request !== $original) {
28295 // This is a terminating redirect response, so set it on the original request
28296 $response->getParams()->set(self::REDIRECT_COUNT, $original->getParams()->get(self::REDIRECT_COUNT));
28297 $original->setResponse($response);
28298 $response->setEffectiveUrl($request->getUrl());
28299 }
28300 return;
28301 }
28302
28303 $this->sendRedirectRequest($original, $request, $response);
28304 }
28305
28306 /**
28307 * Get the original request that initiated a series of redirects
28308 *
28309 * @param RequestInterface $request Request to get the original request from
28310 *
28311 * @return RequestInterface
28312 */
28313 protected function getOriginalRequest(RequestInterface $request)
28314 {
28315 $original = $request;
28316 // The number of redirects is held on the original request, so determine which request that is
28317 while ($parent = $original->getParams()->get(self::PARENT_REQUEST)) {
28318 $original = $parent;
28319 }
28320
28321 return $original;
28322 }
28323
28324 /**
28325 * Create a redirect request for a specific request object
28326 *
28327 * Takes into account strict RFC compliant redirection (e.g. redirect POST with POST) vs doing what most clients do
28328 * (e.g. redirect POST with GET).
28329 *
28330 * @param RequestInterface $request Request being redirected
28331 * @param RequestInterface $original Original request
28332 * @param int $statusCode Status code of the redirect
28333 * @param string $location Location header of the redirect
28334 *
28335 * @return RequestInterface Returns a new redirect request
28336 * @throws CouldNotRewindStreamException If the body needs to be rewound but cannot
28337 */
28338 protected function createRedirectRequest(
28339 RequestInterface $request,
28340 $statusCode,
28341 $location,
28342 RequestInterface $original
28343 ) {
28344 $redirectRequest = null;
28345 $strict = $original->getParams()->get(self::STRICT_REDIRECTS);
28346
28347 // Use a GET request if this is an entity enclosing request and we are not forcing RFC compliance, but rather
28348 // emulating what all browsers would do
28349 if ($request instanceof EntityEnclosingRequestInterface && !$strict && $statusCode <= 302) {
28350 $redirectRequest = RequestFactory::getInstance()->cloneRequestWithMethod($request, 'GET');
28351 } else {
28352 $redirectRequest = clone $request;
28353 }
28354
28355 $redirectRequest->setIsRedirect(true);
28356 // Always use the same response body when redirecting
28357 $redirectRequest->setResponseBody($request->getResponseBody());
28358
28359 $location = Url::factory($location);
28360 // If the location is not absolute, then combine it with the original URL
28361 if (!$location->isAbsolute()) {
28362 $originalUrl = $redirectRequest->getUrl(true);
28363 // Remove query string parameters and just take what is present on the redirect Location header
28364 $originalUrl->getQuery()->clear();
28365 $location = $originalUrl->combine((string) $location);
28366 }
28367
28368 $redirectRequest->setUrl($location);
28369
28370 // Add the parent request to the request before it sends (make sure it's before the onRequestClone event too)
28371 $redirectRequest->getEventDispatcher()->addListener(
28372 'request.before_send',
28373 $func = function ($e) use (&$func, $request, $redirectRequest) {
28374 $redirectRequest->getEventDispatcher()->removeListener('request.before_send', $func);
28375 $e['request']->getParams()->set(RedirectPlugin::PARENT_REQUEST, $request);
28376 }
28377 );
28378
28379 // Rewind the entity body of the request if needed
28380 if ($redirectRequest instanceof EntityEnclosingRequestInterface && $redirectRequest->getBody()) {
28381 $body = $redirectRequest->getBody();
28382 // Only rewind the body if some of it has been read already, and throw an exception if the rewind fails
28383 if ($body->ftell() && !$body->rewind()) {
28384 throw new CouldNotRewindStreamException(
28385 'Unable to rewind the non-seekable entity body of the request after redirecting. cURL probably '
28386 . 'sent part of body before the redirect occurred. Try adding acustom rewind function using on the '
28387 . 'entity body of the request using setRewindFunction().'
28388 );
28389 }
28390 }
28391
28392 return $redirectRequest;
28393 }
28394
28395 /**
28396 * Prepare the request for redirection and enforce the maximum number of allowed redirects per client
28397 *
28398 * @param RequestInterface $original Origina request
28399 * @param RequestInterface $request Request to prepare and validate
28400 * @param Response $response The current response
28401 *
28402 * @return RequestInterface
28403 */
28404 protected function prepareRedirection(RequestInterface $original, RequestInterface $request, Response $response)
28405 {
28406 $params = $original->getParams();
28407 // This is a new redirect, so increment the redirect counter
28408 $current = $params[self::REDIRECT_COUNT] + 1;
28409 $params[self::REDIRECT_COUNT] = $current;
28410 // Use a provided maximum value or default to a max redirect count of 5
28411 $max = isset($params[self::MAX_REDIRECTS]) ? $params[self::MAX_REDIRECTS] : $this->defaultMaxRedirects;
28412
28413 // Throw an exception if the redirect count is exceeded
28414 if ($current > $max) {
28415 $this->throwTooManyRedirectsException($original, $max);
28416 return false;
28417 } else {
28418 // Create a redirect request based on the redirect rules set on the request
28419 return $this->createRedirectRequest(
28420 $request,
28421 $response->getStatusCode(),
28422 trim($response->getLocation()),
28423 $original
28424 );
28425 }
28426 }
28427
28428 /**
28429 * Send a redirect request and handle any errors
28430 *
28431 * @param RequestInterface $original The originating request
28432 * @param RequestInterface $request The current request being redirected
28433 * @param Response $response The response of the current request
28434 *
28435 * @throws BadResponseException|\Exception
28436 */
28437 protected function sendRedirectRequest(RequestInterface $original, RequestInterface $request, Response $response)
28438 {
28439 // Validate and create a redirect request based on the original request and current response
28440 if ($redirectRequest = $this->prepareRedirection($original, $request, $response)) {
28441 try {
28442 $redirectRequest->send();
28443 } catch (BadResponseException $e) {
28444 $e->getResponse();
28445 if (!$e->getResponse()) {
28446 throw $e;
28447 }
28448 }
28449 }
28450 }
28451
28452 /**
28453 * Throw a too many redirects exception for a request
28454 *
28455 * @param RequestInterface $original Request
28456 * @param int $max Max allowed redirects
28457 *
28458 * @throws TooManyRedirectsException when too many redirects have been issued
28459 */
28460 protected function throwTooManyRedirectsException(RequestInterface $original, $max)
28461 {
28462 $original->getEventDispatcher()->addListener(
28463 'request.complete',
28464 $func = function ($e) use (&$func, $original, $max) {
28465 $original->getEventDispatcher()->removeListener('request.complete', $func);
28466 $str = "{$max} redirects were issued for this request:\n" . $e['request']->getRawHeaders();
28467 throw new TooManyRedirectsException($str);
28468 }
28469 );
28470 }
28471 }
28472 <?php
28473
28474 namespace Guzzle\Http;
28475
28476 use Guzzle\Common\Exception\RuntimeException;
28477
28478 /**
28479 * EntityBody decorator that can cache previously read bytes from a sequentially read tstream
28480 */
28481 class CachingEntityBody extends AbstractEntityBodyDecorator
28482 {
28483 /** @var EntityBody Remote stream used to actually pull data onto the buffer */
28484 protected $remoteStream;
28485
28486 /** @var int The number of bytes to skip reading due to a write on the temporary buffer */
28487 protected $skipReadBytes = 0;
28488
28489 /**
28490 * We will treat the buffer object as the body of the entity body
28491 * {@inheritdoc}
28492 */
28493 public function __construct(EntityBodyInterface $body)
28494 {
28495 $this->remoteStream = $body;
28496 $this->body = new EntityBody(fopen('php://temp', 'r+'));
28497 }
28498
28499 /**
28500 * Will give the contents of the buffer followed by the exhausted remote stream.
28501 *
28502 * Warning: Loads the entire stream into memory
28503 *
28504 * @return string
28505 */
28506 public function __toString()
28507 {
28508 $pos = $this->ftell();
28509 $this->rewind();
28510
28511 $str = '';
28512 while (!$this->isConsumed()) {
28513 $str .= $this->read(16384);
28514 }
28515
28516 $this->seek($pos);
28517
28518 return $str;
28519 }
28520
28521 public function getSize()
28522 {
28523 return max($this->body->getSize(), $this->remoteStream->getSize());
28524 }
28525
28526 /**
28527 * {@inheritdoc}
28528 * @throws RuntimeException When seeking with SEEK_END or when seeking past the total size of the buffer stream
28529 */
28530 public function seek($offset, $whence = SEEK_SET)
28531 {
28532 if ($whence == SEEK_SET) {
28533 $byte = $offset;
28534 } elseif ($whence == SEEK_CUR) {
28535 $byte = $offset + $this->ftell();
28536 } else {
28537 throw new RuntimeException(__CLASS__ . ' supports only SEEK_SET and SEEK_CUR seek operations');
28538 }
28539
28540 // You cannot skip ahead past where you've read from the remote stream
28541 if ($byte > $this->body->getSize()) {
28542 throw new RuntimeException(
28543 "Cannot seek to byte {$byte} when the buffered stream only contains {$this->body->getSize()} bytes"
28544 );
28545 }
28546
28547 return $this->body->seek($byte);
28548 }
28549
28550 public function rewind()
28551 {
28552 return $this->seek(0);
28553 }
28554
28555 /**
28556 * Does not support custom rewind functions
28557 *
28558 * @throws RuntimeException
28559 */
28560 public function setRewindFunction($callable)
28561 {
28562 throw new RuntimeException(__CLASS__ . ' does not support custom stream rewind functions');
28563 }
28564
28565 public function read($length)
28566 {
28567 // Perform a regular read on any previously read data from the buffer
28568 $data = $this->body->read($length);
28569 $remaining = $length - strlen($data);
28570
28571 // More data was requested so read from the remote stream
28572 if ($remaining) {
28573 // If data was written to the buffer in a position that would have been filled from the remote stream,
28574 // then we must skip bytes on the remote stream to emulate overwriting bytes from that position. This
28575 // mimics the behavior of other PHP stream wrappers.
28576 $remoteData = $this->remoteStream->read($remaining + $this->skipReadBytes);
28577
28578 if ($this->skipReadBytes) {
28579 $len = strlen($remoteData);
28580 $remoteData = substr($remoteData, $this->skipReadBytes);
28581 $this->skipReadBytes = max(0, $this->skipReadBytes - $len);
28582 }
28583
28584 $data .= $remoteData;
28585 $this->body->write($remoteData);
28586 }
28587
28588 return $data;
28589 }
28590
28591 public function write($string)
28592 {
28593 // When appending to the end of the currently read stream, you'll want to skip bytes from being read from
28594 // the remote stream to emulate other stream wrappers. Basically replacing bytes of data of a fixed length.
28595 $overflow = (strlen($string) + $this->ftell()) - $this->remoteStream->ftell();
28596 if ($overflow > 0) {
28597 $this->skipReadBytes += $overflow;
28598 }
28599
28600 return $this->body->write($string);
28601 }
28602
28603 /**
28604 * {@inheritdoc}
28605 * @link http://php.net/manual/en/function.fgets.php
28606 */
28607 public function readLine($maxLength = null)
28608 {
28609 $buffer = '';
28610 $size = 0;
28611 while (!$this->isConsumed()) {
28612 $byte = $this->read(1);
28613 $buffer .= $byte;
28614 // Break when a new line is found or the max length - 1 is reached
28615 if ($byte == PHP_EOL || ++$size == $maxLength - 1) {
28616 break;
28617 }
28618 }
28619
28620 return $buffer;
28621 }
28622
28623 public function isConsumed()
28624 {
28625 return $this->body->isConsumed() && $this->remoteStream->isConsumed();
28626 }
28627
28628 /**
28629 * Close both the remote stream and buffer stream
28630 */
28631 public function close()
28632 {
28633 return $this->remoteStream->close() && $this->body->close();
28634 }
28635
28636 public function setStream($stream, $size = 0)
28637 {
28638 $this->remoteStream->setStream($stream, $size);
28639 }
28640
28641 public function getContentType()
28642 {
28643 return $this->remoteStream->getContentType();
28644 }
28645
28646 public function getContentEncoding()
28647 {
28648 return $this->remoteStream->getContentEncoding();
28649 }
28650
28651 public function getMetaData($key = null)
28652 {
28653 return $this->remoteStream->getMetaData($key);
28654 }
28655
28656 public function getStream()
28657 {
28658 return $this->remoteStream->getStream();
28659 }
28660
28661 public function getWrapper()
28662 {
28663 return $this->remoteStream->getWrapper();
28664 }
28665
28666 public function getWrapperData()
28667 {
28668 return $this->remoteStream->getWrapperData();
28669 }
28670
28671 public function getStreamType()
28672 {
28673 return $this->remoteStream->getStreamType();
28674 }
28675
28676 public function getUri()
28677 {
28678 return $this->remoteStream->getUri();
28679 }
28680
28681 /**
28682 * Always retrieve custom data from the remote stream
28683 * {@inheritdoc}
28684 */
28685 public function getCustomData($key)
28686 {
28687 return $this->remoteStream->getCustomData($key);
28688 }
28689
28690 /**
28691 * Always set custom data on the remote stream
28692 * {@inheritdoc}
28693 */
28694 public function setCustomData($key, $value)
28695 {
28696 $this->remoteStream->setCustomData($key, $value);
28697
28698 return $this;
28699 }
28700 }
28701 <?php
28702
28703 namespace Guzzle\Http;
28704
28705 use Guzzle\Common\Exception\InvalidArgumentException;
28706 use Guzzle\Parser\ParserRegistry;
28707
28708 /**
28709 * Parses and generates URLs based on URL parts. In favor of performance, URL parts are not validated.
28710 */
28711 class Url
28712 {
28713 protected $scheme;
28714 protected $host;
28715 protected $port;
28716 protected $username;
28717 protected $password;
28718 protected $path = '';
28719 protected $fragment;
28720
28721 /** @var QueryString Query part of the URL */
28722 protected $query;
28723
28724 /**
28725 * Factory method to create a new URL from a URL string
28726 *
28727 * @param string $url Full URL used to create a Url object
28728 *
28729 * @return Url
28730 * @throws InvalidArgumentException
28731 */
28732 public static function factory($url)
28733 {
28734 static $defaults = array('scheme' => null, 'host' => null, 'path' => null, 'port' => null, 'query' => null,
28735 'user' => null, 'pass' => null, 'fragment' => null);
28736
28737 if (false === ($parts = parse_url($url))) {
28738 throw new InvalidArgumentException('Was unable to parse malformed url: ' . $url);
28739 }
28740
28741 $parts += $defaults;
28742
28743 // Convert the query string into a QueryString object
28744 if ($parts['query'] || 0 !== strlen($parts['query'])) {
28745 $parts['query'] = QueryString::fromString($parts['query']);
28746 }
28747
28748 return new self($parts['scheme'], $parts['host'], $parts['user'],
28749 $parts['pass'], $parts['port'], $parts['path'], $parts['query'],
28750 $parts['fragment']);
28751 }
28752
28753 /**
28754 * Build a URL from parse_url parts. The generated URL will be a relative URL if a scheme or host are not provided.
28755 *
28756 * @param array $parts Array of parse_url parts
28757 *
28758 * @return string
28759 */
28760 public static function buildUrl(array $parts)
28761 {
28762 $url = $scheme = '';
28763
28764 if (isset($parts['scheme'])) {
28765 $scheme = $parts['scheme'];
28766 $url .= $scheme . ':';
28767 }
28768
28769 if (isset($parts['host'])) {
28770 $url .= '//';
28771 if (isset($parts['user'])) {
28772 $url .= $parts['user'];
28773 if (isset($parts['pass'])) {
28774 $url .= ':' . $parts['pass'];
28775 }
28776 $url .= '@';
28777 }
28778
28779 $url .= $parts['host'];
28780
28781 // Only include the port if it is not the default port of the scheme
28782 if (isset($parts['port'])
28783 && !(($scheme == 'http' && $parts['port'] == 80) || ($scheme == 'https' && $parts['port'] == 443))
28784 ) {
28785 $url .= ':' . $parts['port'];
28786 }
28787 }
28788
28789 // Add the path component if present
28790 if (isset($parts['path']) && 0 !== strlen($parts['path'])) {
28791 // Always ensure that the path begins with '/' if set and something is before the path
28792 if ($url && $parts['path'][0] != '/' && substr($url, -1) != '/') {
28793 $url .= '/';
28794 }
28795 $url .= $parts['path'];
28796 }
28797
28798 // Add the query string if present
28799 if (isset($parts['query'])) {
28800 $url .= '?' . $parts['query'];
28801 }
28802
28803 // Ensure that # is only added to the url if fragment contains anything.
28804 if (isset($parts['fragment'])) {
28805 $url .= '#' . $parts['fragment'];
28806 }
28807
28808 return $url;
28809 }
28810
28811 /**
28812 * Create a new URL from URL parts
28813 *
28814 * @param string $scheme Scheme of the URL
28815 * @param string $host Host of the URL
28816 * @param string $username Username of the URL
28817 * @param string $password Password of the URL
28818 * @param int $port Port of the URL
28819 * @param string $path Path of the URL
28820 * @param QueryString|array|string $query Query string of the URL
28821 * @param string $fragment Fragment of the URL
28822 */
28823 public function __construct($scheme, $host, $username = null, $password = null, $port = null, $path = null, QueryString $query = null, $fragment = null)
28824 {
28825 $this->scheme = $scheme;
28826 $this->host = $host;
28827 $this->port = $port;
28828 $this->username = $username;
28829 $this->password = $password;
28830 $this->fragment = $fragment;
28831 if (!$query) {
28832 $this->query = new QueryString();
28833 } else {
28834 $this->setQuery($query);
28835 }
28836 $this->setPath($path);
28837 }
28838
28839 /**
28840 * Clone the URL
28841 */
28842 public function __clone()
28843 {
28844 $this->query = clone $this->query;
28845 }
28846
28847 /**
28848 * Returns the URL as a URL string
28849 *
28850 * @return string
28851 */
28852 public function __toString()
28853 {
28854 return self::buildUrl($this->getParts());
28855 }
28856
28857 /**
28858 * Get the parts of the URL as an array
28859 *
28860 * @return array
28861 */
28862 public function getParts()
28863 {
28864 return array(
28865 'scheme' => $this->scheme,
28866 'user' => $this->username,
28867 'pass' => $this->password,
28868 'host' => $this->host,
28869 'port' => $this->port,
28870 'path' => $this->getPath(),
28871 'query' => (string) $this->query ?: null,
28872 'fragment' => $this->fragment,
28873 );
28874 }
28875
28876 /**
28877 * Set the host of the request.
28878 *
28879 * @param string $host Host to set (e.g. www.yahoo.com, yahoo.com)
28880 *
28881 * @return Url
28882 */
28883 public function setHost($host)
28884 {
28885 if (strpos($host, ':') === false) {
28886 $this->host = $host;
28887 } else {
28888 list($host, $port) = explode(':', $host);
28889 $this->host = $host;
28890 $this->setPort($port);
28891 }
28892
28893 return $this;
28894 }
28895
28896 /**
28897 * Get the host part of the URL
28898 *
28899 * @return string
28900 */
28901 public function getHost()
28902 {
28903 return $this->host;
28904 }
28905
28906 /**
28907 * Set the scheme part of the URL (http, https, ftp, etc)
28908 *
28909 * @param string $scheme Scheme to set
28910 *
28911 * @return Url
28912 */
28913 public function setScheme($scheme)
28914 {
28915 $this->scheme = $scheme;
28916
28917 return $this;
28918 }
28919
28920 /**
28921 * Get the scheme part of the URL
28922 *
28923 * @return string
28924 */
28925 public function getScheme()
28926 {
28927 return $this->scheme;
28928 }
28929
28930 /**
28931 * Set the port part of the URL
28932 *
28933 * @param int $port Port to set
28934 *
28935 * @return Url
28936 */
28937 public function setPort($port)
28938 {
28939 $this->port = $port;
28940
28941 return $this;
28942 }
28943
28944 /**
28945 * Get the port part of the URl. Will return the default port for a given scheme if no port has been set.
28946 *
28947 * @return int|null
28948 */
28949 public function getPort()
28950 {
28951 if ($this->port) {
28952 return $this->port;
28953 } elseif ($this->scheme == 'http') {
28954 return 80;
28955 } elseif ($this->scheme == 'https') {
28956 return 443;
28957 }
28958
28959 return null;
28960 }
28961
28962 /**
28963 * Set the path part of the URL
28964 *
28965 * @param array|string $path Path string or array of path segments
28966 *
28967 * @return Url
28968 */
28969 public function setPath($path)
28970 {
28971 static $pathReplace = array(' ' => '%20', '?' => '%3F');
28972 if (is_array($path)) {
28973 $path = '/' . implode('/', $path);
28974 }
28975
28976 $this->path = strtr($path, $pathReplace);
28977
28978 return $this;
28979 }
28980
28981 /**
28982 * Normalize the URL so that double slashes and relative paths are removed
28983 *
28984 * @return Url
28985 */
28986 public function normalizePath()
28987 {
28988 if (!$this->path || $this->path == '/' || $this->path == '*') {
28989 return $this;
28990 }
28991
28992 // Replace // and /./ with /
28993 $this->path = str_replace(array('/./', '//'), '/', $this->path);
28994
28995 // Remove dot segments
28996 if (strpos($this->path, '..') !== false) {
28997
28998 // Remove trailing relative paths if possible
28999 $segments = $this->getPathSegments();
29000 $last = end($segments);
29001 $trailingSlash = false;
29002 if ($last === '') {
29003 array_pop($segments);
29004 $trailingSlash = true;
29005 }
29006
29007 while ($last == '..' || $last == '.') {
29008 if ($last == '..') {
29009 array_pop($segments);
29010 $last = array_pop($segments);
29011 }
29012 if ($last == '.' || $last == '') {
29013 $last = array_pop($segments);
29014 }
29015 }
29016
29017 $this->path = implode('/', $segments);
29018 if ($trailingSlash) {
29019 $this->path .= '/';
29020 }
29021 }
29022
29023 return $this;
29024 }
29025
29026 /**
29027 * Add a relative path to the currently set path
29028 *
29029 * @param string $relativePath Relative path to add
29030 *
29031 * @return Url
29032 */
29033 public function addPath($relativePath)
29034 {
29035 if (!$relativePath || $relativePath == '/') {
29036 return $this;
29037 }
29038
29039 // Add a leading slash if needed
29040 if ($relativePath[0] != '/') {
29041 $relativePath = '/' . $relativePath;
29042 }
29043
29044 return $this->setPath(str_replace('//', '/', $this->getPath() . $relativePath));
29045 }
29046
29047 /**
29048 * Get the path part of the URL
29049 *
29050 * @return string
29051 */
29052 public function getPath()
29053 {
29054 return $this->path;
29055 }
29056
29057 /**
29058 * Get the path segments of the URL as an array
29059 *
29060 * @return array
29061 */
29062 public function getPathSegments()
29063 {
29064 return array_slice(explode('/', $this->getPath()), 1);
29065 }
29066
29067 /**
29068 * Set the password part of the URL
29069 *
29070 * @param string $password Password to set
29071 *
29072 * @return Url
29073 */
29074 public function setPassword($password)
29075 {
29076 $this->password = $password;
29077
29078 return $this;
29079 }
29080
29081 /**
29082 * Get the password part of the URL
29083 *
29084 * @return null|string
29085 */
29086 public function getPassword()
29087 {
29088 return $this->password;
29089 }
29090
29091 /**
29092 * Set the username part of the URL
29093 *
29094 * @param string $username Username to set
29095 *
29096 * @return Url
29097 */
29098 public function setUsername($username)
29099 {
29100 $this->username = $username;
29101
29102 return $this;
29103 }
29104
29105 /**
29106 * Get the username part of the URl
29107 *
29108 * @return null|string
29109 */
29110 public function getUsername()
29111 {
29112 return $this->username;
29113 }
29114
29115 /**
29116 * Get the query part of the URL as a QueryString object
29117 *
29118 * @return QueryString
29119 */
29120 public function getQuery()
29121 {
29122 return $this->query;
29123 }
29124
29125 /**
29126 * Set the query part of the URL
29127 *
29128 * @param QueryString|string|array $query Query to set
29129 *
29130 * @return Url
29131 */
29132 public function setQuery($query)
29133 {
29134 if (is_string($query)) {
29135 $output = null;
29136 parse_str($query, $output);
29137 $this->query = new QueryString($output);
29138 } elseif (is_array($query)) {
29139 $this->query = new QueryString($query);
29140 } elseif ($query instanceof QueryString) {
29141 $this->query = $query;
29142 }
29143
29144 return $this;
29145 }
29146
29147 /**
29148 * Get the fragment part of the URL
29149 *
29150 * @return null|string
29151 */
29152 public function getFragment()
29153 {
29154 return $this->fragment;
29155 }
29156
29157 /**
29158 * Set the fragment part of the URL
29159 *
29160 * @param string $fragment Fragment to set
29161 *
29162 * @return Url
29163 */
29164 public function setFragment($fragment)
29165 {
29166 $this->fragment = $fragment;
29167
29168 return $this;
29169 }
29170
29171 /**
29172 * Check if this is an absolute URL
29173 *
29174 * @return bool
29175 */
29176 public function isAbsolute()
29177 {
29178 return $this->scheme && $this->host;
29179 }
29180
29181 /**
29182 * Combine the URL with another URL. Follows the rules specific in RFC 3986 section 5.4.
29183 *
29184 * @param string $url Relative URL to combine with
29185 *
29186 * @return Url
29187 * @throws InvalidArgumentException
29188 * @link http://tools.ietf.org/html/rfc3986#section-5.4
29189 */
29190 public function combine($url)
29191 {
29192 $url = self::factory($url);
29193
29194 // Use the more absolute URL as the base URL
29195 if (!$this->isAbsolute() && $url->isAbsolute()) {
29196 $url = $url->combine($this);
29197 }
29198
29199 // Passing a URL with a scheme overrides everything
29200 if ($buffer = $url->getScheme()) {
29201 $this->scheme = $buffer;
29202 $this->host = $url->getHost();
29203 $this->port = $url->getPort();
29204 $this->username = $url->getUsername();
29205 $this->password = $url->getPassword();
29206 $this->path = $url->getPath();
29207 $this->query = $url->getQuery();
29208 $this->fragment = $url->getFragment();
29209 return $this;
29210 }
29211
29212 // Setting a host overrides the entire rest of the URL
29213 if ($buffer = $url->getHost()) {
29214 $this->host = $buffer;
29215 $this->port = $url->getPort();
29216 $this->username = $url->getUsername();
29217 $this->password = $url->getPassword();
29218 $this->path = $url->getPath();
29219 $this->fragment = $url->getFragment();
29220 return $this;
29221 }
29222
29223 $path = $url->getPath();
29224 $query = $url->getQuery();
29225
29226 if (!$path) {
29227 if (count($query)) {
29228 $this->query = $query;
29229 }
29230 } else {
29231 if ($path[0] == '/') {
29232 $this->path = $path;
29233 } else {
29234 $this->path .= '/' . $path;
29235 }
29236 $this->normalizePath();
29237 $this->query = $query;
29238 }
29239
29240 $this->fragment = $url->getFragment();
29241
29242 return $this;
29243 }
29244 }
29245 <?php
29246
29247 namespace Guzzle\Http;
29248
29249 use Guzzle\Common\HasDispatcherInterface;
29250 use Guzzle\Common\Collection;
29251 use Guzzle\Common\Exception\InvalidArgumentException;
29252 use Guzzle\Http\Message\EntityEnclosingRequestInterface;
29253 use Guzzle\Http\Message\RequestInterface;
29254
29255 /**
29256 * Client interface for send HTTP requests
29257 */
29258 interface ClientInterface extends HasDispatcherInterface
29259 {
29260 const CREATE_REQUEST = 'client.create_request';
29261
29262 /** @var string RFC 1123 HTTP-Date */
29263 const HTTP_DATE = 'D, d M Y H:i:s \G\M\T';
29264
29265 /**
29266 * Set the configuration object to use with the client
29267 *
29268 * @param array|Collection $config Parameters that define how the client behaves
29269 *
29270 * @return self
29271 */
29272 public function setConfig($config);
29273
29274 /**
29275 * Get a configuration setting or all of the configuration settings. The Collection result of this method can be
29276 * modified to change the configuration settings of a client.
29277 *
29278 * A client should honor the following special values:
29279 *
29280 * - request.options: Associative array of default RequestFactory options to apply to each request
29281 * - request.params: Associative array of request parameters (data values) to apply to each request
29282 * - curl.options: Associative array of cURL configuration settings to apply to each request
29283 * - ssl.certificate_authority: Path a CAINFO, CAPATH, true to use strict defaults, or false to disable verification
29284 * - redirect.disable: Set to true to disable redirects
29285 *
29286 * @param bool|string $key Configuration value to retrieve. Set to FALSE to retrieve all values of the client.
29287 * The object return can be modified, and modifications will affect the client's config.
29288 * @return mixed|Collection
29289 * @see \Guzzle\Http\Message\RequestFactoryInterface::applyOptions for a full list of request.options options
29290 */
29291 public function getConfig($key = false);
29292
29293 /**
29294 * Create and return a new {@see RequestInterface} configured for the client.
29295 *
29296 * Use an absolute path to override the base path of the client, or a relative path to append to the base path of
29297 * the client. The URI can contain the query string as well. Use an array to provide a URI template and additional
29298 * variables to use in the URI template expansion.
29299 *
29300 * @param string $method HTTP method. Defaults to GET
29301 * @param string|array $uri Resource URI.
29302 * @param array|Collection $headers HTTP headers
29303 * @param string|resource|array|EntityBodyInterface $body Entity body of request (POST/PUT) or response (GET)
29304 * @param array $options Array of options to apply to the request
29305 *
29306 * @return RequestInterface
29307 * @throws InvalidArgumentException if a URI array is passed that does not contain exactly two elements: the URI
29308 * followed by template variables
29309 */
29310 public function createRequest(
29311 $method = RequestInterface::GET,
29312 $uri = null,
29313 $headers = null,
29314 $body = null,
29315 array $options = array()
29316 );
29317
29318 /**
29319 * Create a GET request for the client
29320 *
29321 * @param string|array $uri Resource URI
29322 * @param array|Collection $headers HTTP headers
29323 * @param array $options Options to apply to the request. For BC compatibility, you can also pass a
29324 * string to tell Guzzle to download the body of the response to a particular
29325 * location. Use the 'body' option instead for forward compatibility.
29326 * @return RequestInterface
29327 * @see Guzzle\Http\ClientInterface::createRequest()
29328 */
29329 public function get($uri = null, $headers = null, $options = array());
29330
29331 /**
29332 * Create a HEAD request for the client
29333 *
29334 * @param string|array $uri Resource URI
29335 * @param array|Collection $headers HTTP headers
29336 * @param array $options Options to apply to the request
29337 *
29338 * @return RequestInterface
29339 * @see Guzzle\Http\ClientInterface::createRequest()
29340 */
29341 public function head($uri = null, $headers = null, array $options = array());
29342
29343 /**
29344 * Create a DELETE request for the client
29345 *
29346 * @param string|array $uri Resource URI
29347 * @param array|Collection $headers HTTP headers
29348 * @param string|resource|EntityBodyInterface $body Body to send in the request
29349 * @param array $options Options to apply to the request
29350 *
29351 * @return EntityEnclosingRequestInterface
29352 * @see Guzzle\Http\ClientInterface::createRequest()
29353 */
29354 public function delete($uri = null, $headers = null, $body = null, array $options = array());
29355
29356 /**
29357 * Create a PUT request for the client
29358 *
29359 * @param string|array $uri Resource URI
29360 * @param array|Collection $headers HTTP headers
29361 * @param string|resource|EntityBodyInterface $body Body to send in the request
29362 * @param array $options Options to apply to the request
29363 *
29364 * @return EntityEnclosingRequestInterface
29365 * @see Guzzle\Http\ClientInterface::createRequest()
29366 */
29367 public function put($uri = null, $headers = null, $body = null, array $options = array());
29368
29369 /**
29370 * Create a PATCH request for the client
29371 *
29372 * @param string|array $uri Resource URI
29373 * @param array|Collection $headers HTTP headers
29374 * @param string|resource|EntityBodyInterface $body Body to send in the request
29375 * @param array $options Options to apply to the request
29376 *
29377 * @return EntityEnclosingRequestInterface
29378 * @see Guzzle\Http\ClientInterface::createRequest()
29379 */
29380 public function patch($uri = null, $headers = null, $body = null, array $options = array());
29381
29382 /**
29383 * Create a POST request for the client
29384 *
29385 * @param string|array $uri Resource URI
29386 * @param array|Collection $headers HTTP headers
29387 * @param array|Collection|string|EntityBodyInterface $postBody POST body. Can be a string, EntityBody, or
29388 * associative array of POST fields to send in the body of the
29389 * request. Prefix a value in the array with the @ symbol to
29390 * reference a file.
29391 * @param array $options Options to apply to the request
29392 *
29393 * @return EntityEnclosingRequestInterface
29394 * @see Guzzle\Http\ClientInterface::createRequest()
29395 */
29396 public function post($uri = null, $headers = null, $postBody = null, array $options = array());
29397
29398 /**
29399 * Create an OPTIONS request for the client
29400 *
29401 * @param string|array $uri Resource URI
29402 * @param array $options Options to apply to the request
29403 *
29404 * @return RequestInterface
29405 * @see Guzzle\Http\ClientInterface::createRequest()
29406 */
29407 public function options($uri = null, array $options = array());
29408
29409 /**
29410 * Sends a single request or an array of requests in parallel
29411 *
29412 * @param array|RequestInterface $requests One or more RequestInterface objects to send
29413 *
29414 * @return \Guzzle\Http\Message\Response|array Returns a single Response or an array of Response objects
29415 */
29416 public function send($requests);
29417
29418 /**
29419 * Get the client's base URL as either an expanded or raw URI template
29420 *
29421 * @param bool $expand Set to FALSE to get the raw base URL without URI template expansion
29422 *
29423 * @return string|null
29424 */
29425 public function getBaseUrl($expand = true);
29426
29427 /**
29428 * Set the base URL of the client
29429 *
29430 * @param string $url The base service endpoint URL of the webservice
29431 *
29432 * @return self
29433 */
29434 public function setBaseUrl($url);
29435
29436 /**
29437 * Set the User-Agent header to be used on all requests from the client
29438 *
29439 * @param string $userAgent User agent string
29440 * @param bool $includeDefault Set to true to prepend the value to Guzzle's default user agent string
29441 *
29442 * @return self
29443 */
29444 public function setUserAgent($userAgent, $includeDefault = false);
29445
29446 /**
29447 * Set SSL verification options.
29448 *
29449 * Setting $certificateAuthority to TRUE will result in the bundled cacert.pem being used to verify against the
29450 * remote host.
29451 *
29452 * Alternate certificates to verify against can be specified with the $certificateAuthority option set to the full
29453 * path to a certificate file, or the path to a directory containing certificates.
29454 *
29455 * Setting $certificateAuthority to FALSE will turn off peer verification, unset the bundled cacert.pem, and
29456 * disable host verification. Please don't do this unless you really know what you're doing, and why you're doing
29457 * it.
29458 *
29459 * @param string|bool $certificateAuthority bool, file path, or directory path
29460 * @param bool $verifyPeer FALSE to stop from verifying the peer's certificate.
29461 * @param int $verifyHost Set to 1 to check the existence of a common name in the SSL peer
29462 * certificate. 2 to check the existence of a common name and also verify
29463 * that it matches the hostname provided.
29464 * @return self
29465 */
29466 public function setSslVerification($certificateAuthority = true, $verifyPeer = true, $verifyHost = 2);
29467 }
29468 <?php
29469
29470 namespace Guzzle\Http;
29471
29472 use Guzzle\Http\Client;
29473 use Guzzle\Http\ClientInterface;
29474 use Guzzle\Stream\StreamRequestFactoryInterface;
29475 use Guzzle\Stream\PhpStreamRequestFactory;
29476
29477 /**
29478 * Simplified interface to Guzzle that does not require a class to be instantiated
29479 */
29480 final class StaticClient
29481 {
29482 /** @var Client Guzzle client */
29483 private static $client;
29484
29485 /**
29486 * Mount the client to a simpler class name for a specific client
29487 *
29488 * @param string $className Class name to use to mount
29489 * @param ClientInterface $client Client used to send requests
29490 */
29491 public static function mount($className = 'Guzzle', ClientInterface $client = null)
29492 {
29493 class_alias(__CLASS__, $className);
29494 if ($client) {
29495 self::$client = $client;
29496 }
29497 }
29498
29499 /**
29500 * @param string $method HTTP request method (GET, POST, HEAD, DELETE, PUT, etc)
29501 * @param string $url URL of the request
29502 * @param array $options Options to use with the request. See: Guzzle\Http\Message\RequestFactory::applyOptions()
29503 * @return \Guzzle\Http\Message\Response|\Guzzle\Stream\Stream
29504 */
29505 public static function request($method, $url, $options = array())
29506 {
29507 // @codeCoverageIgnoreStart
29508 if (!self::$client) {
29509 self::$client = new Client();
29510 }
29511 // @codeCoverageIgnoreEnd
29512
29513 $request = self::$client->createRequest($method, $url, null, null, $options);
29514
29515 if (isset($options['stream'])) {
29516 if ($options['stream'] instanceof StreamRequestFactoryInterface) {
29517 return $options['stream']->fromRequest($request);
29518 } elseif ($options['stream'] == true) {
29519 $streamFactory = new PhpStreamRequestFactory();
29520 return $streamFactory->fromRequest($request);
29521 }
29522 }
29523
29524 return $request->send();
29525 }
29526
29527 /**
29528 * Send a GET request
29529 *
29530 * @param string $url URL of the request
29531 * @param array $options Array of request options
29532 *
29533 * @return \Guzzle\Http\Message\Response
29534 * @see Guzzle::request for a list of available options
29535 */
29536 public static function get($url, $options = array())
29537 {
29538 return self::request('GET', $url, $options);
29539 }
29540
29541 /**
29542 * Send a HEAD request
29543 *
29544 * @param string $url URL of the request
29545 * @param array $options Array of request options
29546 *
29547 * @return \Guzzle\Http\Message\Response
29548 * @see Guzzle::request for a list of available options
29549 */
29550 public static function head($url, $options = array())
29551 {
29552 return self::request('HEAD', $url, $options);
29553 }
29554
29555 /**
29556 * Send a DELETE request
29557 *
29558 * @param string $url URL of the request
29559 * @param array $options Array of request options
29560 *
29561 * @return \Guzzle\Http\Message\Response
29562 * @see Guzzle::request for a list of available options
29563 */
29564 public static function delete($url, $options = array())
29565 {
29566 return self::request('DELETE', $url, $options);
29567 }
29568
29569 /**
29570 * Send a POST request
29571 *
29572 * @param string $url URL of the request
29573 * @param array $options Array of request options
29574 *
29575 * @return \Guzzle\Http\Message\Response
29576 * @see Guzzle::request for a list of available options
29577 */
29578 public static function post($url, $options = array())
29579 {
29580 return self::request('POST', $url, $options);
29581 }
29582
29583 /**
29584 * Send a PUT request
29585 *
29586 * @param string $url URL of the request
29587 * @param array $options Array of request options
29588 *
29589 * @return \Guzzle\Http\Message\Response
29590 * @see Guzzle::request for a list of available options
29591 */
29592 public static function put($url, $options = array())
29593 {
29594 return self::request('PUT', $url, $options);
29595 }
29596
29597 /**
29598 * Send a PATCH request
29599 *
29600 * @param string $url URL of the request
29601 * @param array $options Array of request options
29602 *
29603 * @return \Guzzle\Http\Message\Response
29604 * @see Guzzle::request for a list of available options
29605 */
29606 public static function patch($url, $options = array())
29607 {
29608 return self::request('PATCH', $url, $options);
29609 }
29610
29611 /**
29612 * Send an OPTIONS request
29613 *
29614 * @param string $url URL of the request
29615 * @param array $options Array of request options
29616 *
29617 * @return \Guzzle\Http\Message\Response
29618 * @see Guzzle::request for a list of available options
29619 */
29620 public static function options($url, $options = array())
29621 {
29622 return self::request('OPTIONS', $url, $options);
29623 }
29624 }
29625 <?php
29626
29627 namespace Guzzle\Http;
29628
29629 use Guzzle\Stream\Stream;
29630
29631 /**
29632 * Abstract decorator used to wrap entity bodies
29633 */
29634 class AbstractEntityBodyDecorator implements EntityBodyInterface
29635 {
29636 /** @var EntityBodyInterface Decorated entity body */
29637 protected $body;
29638
29639 /**
29640 * @param EntityBodyInterface $body Entity body to decorate
29641 */
29642 public function __construct(EntityBodyInterface $body)
29643 {
29644 $this->body = $body;
29645 }
29646
29647 public function __toString()
29648 {
29649 return (string) $this->body;
29650 }
29651
29652 /**
29653 * Allow decorators to implement custom methods
29654 *
29655 * @param string $method Missing method name
29656 * @param array $args Method arguments
29657 *
29658 * @return mixed
29659 */
29660 public function __call($method, array $args)
29661 {
29662 return call_user_func_array(array($this->body, $method), $args);
29663 }
29664
29665 public function close()
29666 {
29667 return $this->body->close();
29668 }
29669
29670 public function setRewindFunction($callable)
29671 {
29672 $this->body->setRewindFunction($callable);
29673
29674 return $this;
29675 }
29676
29677 public function rewind()
29678 {
29679 return $this->body->rewind();
29680 }
29681
29682 public function compress($filter = 'zlib.deflate')
29683 {
29684 return $this->body->compress($filter);
29685 }
29686
29687 public function uncompress($filter = 'zlib.inflate')
29688 {
29689 return $this->body->uncompress($filter);
29690 }
29691
29692 public function getContentLength()
29693 {
29694 return $this->getSize();
29695 }
29696
29697 public function getContentType()
29698 {
29699 return $this->body->getContentType();
29700 }
29701
29702 public function getContentMd5($rawOutput = false, $base64Encode = false)
29703 {
29704 $hash = Stream::getHash($this, 'md5', $rawOutput);
29705
29706 return $hash && $base64Encode ? base64_encode($hash) : $hash;
29707 }
29708
29709 public function getContentEncoding()
29710 {
29711 return $this->body->getContentEncoding();
29712 }
29713
29714 public function getMetaData($key = null)
29715 {
29716 return $this->body->getMetaData($key);
29717 }
29718
29719 public function getStream()
29720 {
29721 return $this->body->getStream();
29722 }
29723
29724 public function setStream($stream, $size = 0)
29725 {
29726 $this->body->setStream($stream, $size);
29727
29728 return $this;
29729 }
29730
29731 public function detachStream()
29732 {
29733 $this->body->detachStream();
29734
29735 return $this;
29736 }
29737
29738 public function getWrapper()
29739 {
29740 return $this->body->getWrapper();
29741 }
29742
29743 public function getWrapperData()
29744 {
29745 return $this->body->getWrapperData();
29746 }
29747
29748 public function getStreamType()
29749 {
29750 return $this->body->getStreamType();
29751 }
29752
29753 public function getUri()
29754 {
29755 return $this->body->getUri();
29756 }
29757
29758 public function getSize()
29759 {
29760 return $this->body->getSize();
29761 }
29762
29763 public function isReadable()
29764 {
29765 return $this->body->isReadable();
29766 }
29767
29768 public function isRepeatable()
29769 {
29770 return $this->isSeekable() && $this->isReadable();
29771 }
29772
29773 public function isWritable()
29774 {
29775 return $this->body->isWritable();
29776 }
29777
29778 public function isConsumed()
29779 {
29780 return $this->body->isConsumed();
29781 }
29782
29783 /**
29784 * Alias of isConsumed()
29785 * {@inheritdoc}
29786 */
29787 public function feof()
29788 {
29789 return $this->isConsumed();
29790 }
29791
29792 public function isLocal()
29793 {
29794 return $this->body->isLocal();
29795 }
29796
29797 public function isSeekable()
29798 {
29799 return $this->body->isSeekable();
29800 }
29801
29802 public function setSize($size)
29803 {
29804 $this->body->setSize($size);
29805
29806 return $this;
29807 }
29808
29809 public function seek($offset, $whence = SEEK_SET)
29810 {
29811 return $this->body->seek($offset, $whence);
29812 }
29813
29814 public function read($length)
29815 {
29816 return $this->body->read($length);
29817 }
29818
29819 public function write($string)
29820 {
29821 return $this->body->write($string);
29822 }
29823
29824 public function readLine($maxLength = null)
29825 {
29826 return $this->body->readLine($maxLength);
29827 }
29828
29829 public function ftell()
29830 {
29831 return $this->body->ftell();
29832 }
29833
29834 public function getCustomData($key)
29835 {
29836 return $this->body->getCustomData($key);
29837 }
29838
29839 public function setCustomData($key, $value)
29840 {
29841 $this->body->setCustomData($key, $value);
29842
29843 return $this;
29844 }
29845 }
29846 <?php
29847
29848 namespace Guzzle\Http;
29849
29850 use Guzzle\Common\Collection;
29851 use Guzzle\Common\AbstractHasDispatcher;
29852 use Guzzle\Common\Exception\ExceptionCollection;
29853 use Guzzle\Common\Exception\InvalidArgumentException;
29854 use Guzzle\Common\Exception\RuntimeException;
29855 use Guzzle\Common\Version;
29856 use Guzzle\Parser\ParserRegistry;
29857 use Guzzle\Parser\UriTemplate\UriTemplateInterface;
29858 use Guzzle\Http\Message\RequestInterface;
29859 use Guzzle\Http\Message\RequestFactory;
29860 use Guzzle\Http\Message\RequestFactoryInterface;
29861 use Guzzle\Http\Curl\CurlMultiInterface;
29862 use Guzzle\Http\Curl\CurlMultiProxy;
29863 use Guzzle\Http\Curl\CurlHandle;
29864 use Guzzle\Http\Curl\CurlVersion;
29865
29866 /**
29867 * HTTP client
29868 */
29869 class Client extends AbstractHasDispatcher implements ClientInterface
29870 {
29871 /** @deprecated Use [request.options][params] */
29872 const REQUEST_PARAMS = 'request.params';
29873
29874 const REQUEST_OPTIONS = 'request.options';
29875 const CURL_OPTIONS = 'curl.options';
29876 const SSL_CERT_AUTHORITY = 'ssl.certificate_authority';
29877 const DISABLE_REDIRECTS = RedirectPlugin::DISABLE;
29878
29879 /** @var Collection Default HTTP headers to set on each request */
29880 protected $defaultHeaders;
29881
29882 /** @var string The user agent string to set on each request */
29883 protected $userAgent;
29884
29885 /** @var Collection Parameter object holding configuration data */
29886 private $config;
29887
29888 /** @var Url Base URL of the client */
29889 private $baseUrl;
29890
29891 /** @var CurlMultiInterface CurlMulti object used internally */
29892 private $curlMulti;
29893
29894 /** @var UriTemplateInterface URI template owned by the client */
29895 private $uriTemplate;
29896
29897 /** @var RequestFactoryInterface Request factory used by the client */
29898 protected $requestFactory;
29899
29900 public static function getAllEvents()
29901 {
29902 return array(self::CREATE_REQUEST);
29903 }
29904
29905 /**
29906 * @param string $baseUrl Base URL of the web service
29907 * @param array|Collection $config Configuration settings
29908 *
29909 * @throws RuntimeException if cURL is not installed
29910 */
29911 public function __construct($baseUrl = '', $config = null)
29912 {
29913 if (!extension_loaded('curl')) {
29914 // @codeCoverageIgnoreStart
29915 throw new RuntimeException('The PHP cURL extension must be installed to use Guzzle.');
29916 // @codeCoverageIgnoreEnd
29917 }
29918 $this->setConfig($config ?: new Collection());
29919 $this->initSsl();
29920 $this->setBaseUrl($baseUrl);
29921 $this->defaultHeaders = new Collection();
29922 $this->setRequestFactory(RequestFactory::getInstance());
29923 $this->userAgent = $this->getDefaultUserAgent();
29924 if (!$this->config[self::DISABLE_REDIRECTS]) {
29925 $this->addSubscriber(new RedirectPlugin());
29926 }
29927 }
29928
29929 final public function setConfig($config)
29930 {
29931 if ($config instanceof Collection) {
29932 $this->config = $config;
29933 } elseif (is_array($config)) {
29934 $this->config = new Collection($config);
29935 } else {
29936 throw new InvalidArgumentException('Config must be an array or Collection');
29937 }
29938
29939 return $this;
29940 }
29941
29942 final public function getConfig($key = false)
29943 {
29944 return $key ? $this->config[$key] : $this->config;
29945 }
29946
29947 /**
29948 * Set a default request option on the client that will be used as a default for each request
29949 *
29950 * @param string $keyOrPath request.options key (e.g. allow_redirects) or path to a nested key (e.g. headers/foo)
29951 * @param mixed $value Value to set
29952 *
29953 * @return $this
29954 */
29955 public function setDefaultOption($keyOrPath, $value)
29956 {
29957 $keyOrPath = self::REQUEST_OPTIONS . '/' . $keyOrPath;
29958 $this->config->setPath($keyOrPath, $value);
29959
29960 return $this;
29961 }
29962
29963 /**
29964 * Retrieve a default request option from the client
29965 *
29966 * @param string $keyOrPath request.options key (e.g. allow_redirects) or path to a nested key (e.g. headers/foo)
29967 *
29968 * @return mixed|null
29969 */
29970 public function getDefaultOption($keyOrPath)
29971 {
29972 $keyOrPath = self::REQUEST_OPTIONS . '/' . $keyOrPath;
29973
29974 return $this->config->getPath($keyOrPath);
29975 }
29976
29977 final public function setSslVerification($certificateAuthority = true, $verifyPeer = true, $verifyHost = 2)
29978 {
29979 $opts = $this->config[self::CURL_OPTIONS] ?: array();
29980
29981 if ($certificateAuthority === true) {
29982 // use bundled CA bundle, set secure defaults
29983 $opts[CURLOPT_CAINFO] = __DIR__ . '/Resources/cacert.pem';
29984 $opts[CURLOPT_SSL_VERIFYPEER] = true;
29985 $opts[CURLOPT_SSL_VERIFYHOST] = 2;
29986 } elseif ($certificateAuthority === false) {
29987 unset($opts[CURLOPT_CAINFO]);
29988 $opts[CURLOPT_SSL_VERIFYPEER] = false;
29989 $opts[CURLOPT_SSL_VERIFYHOST] = 2;
29990 } elseif ($verifyPeer !== true && $verifyPeer !== false && $verifyPeer !== 1 && $verifyPeer !== 0) {
29991 throw new InvalidArgumentException('verifyPeer must be 1, 0 or boolean');
29992 } elseif ($verifyHost !== 0 && $verifyHost !== 1 && $verifyHost !== 2) {
29993 throw new InvalidArgumentException('verifyHost must be 0, 1 or 2');
29994 } else {
29995 $opts[CURLOPT_SSL_VERIFYPEER] = $verifyPeer;
29996 $opts[CURLOPT_SSL_VERIFYHOST] = $verifyHost;
29997 if (is_file($certificateAuthority)) {
29998 unset($opts[CURLOPT_CAPATH]);
29999 $opts[CURLOPT_CAINFO] = $certificateAuthority;
30000 } elseif (is_dir($certificateAuthority)) {
30001 unset($opts[CURLOPT_CAINFO]);
30002 $opts[CURLOPT_CAPATH] = $certificateAuthority;
30003 } else {
30004 throw new RuntimeException(
30005 'Invalid option passed to ' . self::SSL_CERT_AUTHORITY . ': ' . $certificateAuthority
30006 );
30007 }
30008 }
30009
30010 $this->config->set(self::CURL_OPTIONS, $opts);
30011
30012 return $this;
30013 }
30014
30015 public function createRequest($method = 'GET', $uri = null, $headers = null, $body = null, array $options = array())
30016 {
30017 if (!$uri) {
30018 $url = $this->getBaseUrl();
30019 } else {
30020 if (!is_array($uri)) {
30021 $templateVars = null;
30022 } else {
30023 list($uri, $templateVars) = $uri;
30024 }
30025 if (substr($uri, 0, 4) === 'http') {
30026 // Use absolute URLs as-is
30027 $url = $this->expandTemplate($uri, $templateVars);
30028 } else {
30029 $url = Url::factory($this->getBaseUrl())->combine($this->expandTemplate($uri, $templateVars));
30030 }
30031 }
30032
30033 // If default headers are provided, then merge them under any explicitly provided headers for the request
30034 if (count($this->defaultHeaders)) {
30035 if (!$headers) {
30036 $headers = $this->defaultHeaders->toArray();
30037 } elseif (is_array($headers)) {
30038 $headers += $this->defaultHeaders->toArray();
30039 } elseif ($headers instanceof Collection) {
30040 $headers = $headers->toArray() + $this->defaultHeaders->toArray();
30041 }
30042 }
30043
30044 return $this->prepareRequest($this->requestFactory->create($method, (string) $url, $headers, $body), $options);
30045 }
30046
30047 public function getBaseUrl($expand = true)
30048 {
30049 return $expand ? $this->expandTemplate($this->baseUrl) : $this->baseUrl;
30050 }
30051
30052 public function setBaseUrl($url)
30053 {
30054 $this->baseUrl = $url;
30055
30056 return $this;
30057 }
30058
30059 public function setUserAgent($userAgent, $includeDefault = false)
30060 {
30061 if ($includeDefault) {
30062 $userAgent .= ' ' . $this->getDefaultUserAgent();
30063 }
30064 $this->userAgent = $userAgent;
30065
30066 return $this;
30067 }
30068
30069 /**
30070 * Get the default User-Agent string to use with Guzzle
30071 *
30072 * @return string
30073 */
30074 public function getDefaultUserAgent()
30075 {
30076 return 'Guzzle/' . Version::VERSION
30077 . ' curl/' . CurlVersion::getInstance()->get('version')
30078 . ' PHP/' . PHP_VERSION;
30079 }
30080
30081 public function get($uri = null, $headers = null, $options = array())
30082 {
30083 // BC compat: $options can be a string, resource, etc to specify where the response body is downloaded
30084 return is_array($options)
30085 ? $this->createRequest('GET', $uri, $headers, null, $options)
30086 : $this->createRequest('GET', $uri, $headers, $options);
30087 }
30088
30089 public function head($uri = null, $headers = null, array $options = array())
30090 {
30091 return $this->createRequest('HEAD', $uri, $headers, null, $options);
30092 }
30093
30094 public function delete($uri = null, $headers = null, $body = null, array $options = array())
30095 {
30096 return $this->createRequest('DELETE', $uri, $headers, $body, $options);
30097 }
30098
30099 public function put($uri = null, $headers = null, $body = null, array $options = array())
30100 {
30101 return $this->createRequest('PUT', $uri, $headers, $body, $options);
30102 }
30103
30104 public function patch($uri = null, $headers = null, $body = null, array $options = array())
30105 {
30106 return $this->createRequest('PATCH', $uri, $headers, $body, $options);
30107 }
30108
30109 public function post($uri = null, $headers = null, $postBody = null, array $options = array())
30110 {
30111 return $this->createRequest('POST', $uri, $headers, $postBody, $options);
30112 }
30113
30114 public function options($uri = null, array $options = array())
30115 {
30116 return $this->createRequest('OPTIONS', $uri, $options);
30117 }
30118
30119 public function send($requests)
30120 {
30121 if (!($requests instanceof RequestInterface)) {
30122 return $this->sendMultiple($requests);
30123 }
30124
30125 try {
30126 /** @var $requests RequestInterface */
30127 $this->getCurlMulti()->add($requests)->send();
30128 return $requests->getResponse();
30129 } catch (ExceptionCollection $e) {
30130 throw $e->getFirst();
30131 }
30132 }
30133
30134 /**
30135 * Set a curl multi object to be used internally by the client for transferring requests.
30136 *
30137 * @param CurlMultiInterface $curlMulti Multi object
30138 *
30139 * @return self
30140 */
30141 public function setCurlMulti(CurlMultiInterface $curlMulti)
30142 {
30143 $this->curlMulti = $curlMulti;
30144
30145 return $this;
30146 }
30147
30148 /**
30149 * @return CurlMultiInterface|CurlMultiProxy
30150 */
30151 public function getCurlMulti()
30152 {
30153 if (!$this->curlMulti) {
30154 $this->curlMulti = new CurlMultiProxy();
30155 }
30156
30157 return $this->curlMulti;
30158 }
30159
30160 public function setRequestFactory(RequestFactoryInterface $factory)
30161 {
30162 $this->requestFactory = $factory;
30163
30164 return $this;
30165 }
30166
30167 /**
30168 * Set the URI template expander to use with the client
30169 *
30170 * @param UriTemplateInterface $uriTemplate URI template expander
30171 *
30172 * @return self
30173 */
30174 public function setUriTemplate(UriTemplateInterface $uriTemplate)
30175 {
30176 $this->uriTemplate = $uriTemplate;
30177
30178 return $this;
30179 }
30180
30181 /**
30182 * Copy the cacert.pem file from the phar if it is not in the temp folder and validate the MD5 checksum
30183 *
30184 * @param bool $md5Check Set to false to not perform the MD5 validation
30185 *
30186 * @return string Returns the path to the extracted cacert
30187 * @throws RuntimeException if the file cannot be copied or there is a MD5 mismatch
30188 */
30189 public function preparePharCacert($md5Check = true)
30190 {
30191 $from = __DIR__ . '/Resources/cacert.pem';
30192 $certFile = sys_get_temp_dir() . '/guzzle-cacert.pem';
30193 if (!file_exists($certFile) && !copy($from, $certFile)) {
30194 throw new RuntimeException("Could not copy {$from} to {$certFile}: " . var_export(error_get_last(), true));
30195 } elseif ($md5Check) {
30196 $actualMd5 = md5_file($certFile);
30197 $expectedMd5 = trim(file_get_contents("{$from}.md5"));
30198 if ($actualMd5 != $expectedMd5) {
30199 throw new RuntimeException("{$certFile} MD5 mismatch: expected {$expectedMd5} but got {$actualMd5}");
30200 }
30201 }
30202
30203 return $certFile;
30204 }
30205
30206 /**
30207 * Expand a URI template while merging client config settings into the template variables
30208 *
30209 * @param string $template Template to expand
30210 * @param array $variables Variables to inject
30211 *
30212 * @return string
30213 */
30214 protected function expandTemplate($template, array $variables = null)
30215 {
30216 $expansionVars = $this->getConfig()->toArray();
30217 if ($variables) {
30218 $expansionVars = $variables + $expansionVars;
30219 }
30220
30221 return $this->getUriTemplate()->expand($template, $expansionVars);
30222 }
30223
30224 /**
30225 * Get the URI template expander used by the client
30226 *
30227 * @return UriTemplateInterface
30228 */
30229 protected function getUriTemplate()
30230 {
30231 if (!$this->uriTemplate) {
30232 $this->uriTemplate = ParserRegistry::getInstance()->getParser('uri_template');
30233 }
30234
30235 return $this->uriTemplate;
30236 }
30237
30238 /**
30239 * Send multiple requests in parallel
30240 *
30241 * @param array $requests Array of RequestInterface objects
30242 *
30243 * @return array Returns an array of Response objects
30244 */
30245 protected function sendMultiple(array $requests)
30246 {
30247 $curlMulti = $this->getCurlMulti();
30248 foreach ($requests as $request) {
30249 $curlMulti->add($request);
30250 }
30251 $curlMulti->send();
30252
30253 /** @var $request RequestInterface */
30254 $result = array();
30255 foreach ($requests as $request) {
30256 $result[] = $request->getResponse();
30257 }
30258
30259 return $result;
30260 }
30261
30262 /**
30263 * Prepare a request to be sent from the Client by adding client specific behaviors and properties to the request.
30264 *
30265 * @param RequestInterface $request Request to prepare for the client
30266 * @param array $options Options to apply to the request
30267 *
30268 * @return RequestInterface
30269 */
30270 protected function prepareRequest(RequestInterface $request, array $options = array())
30271 {
30272 $request->setClient($this)->setEventDispatcher(clone $this->getEventDispatcher());
30273
30274 if ($curl = $this->config[self::CURL_OPTIONS]) {
30275 $request->getCurlOptions()->overwriteWith(CurlHandle::parseCurlConfig($curl));
30276 }
30277
30278 if ($params = $this->config[self::REQUEST_PARAMS]) {
30279 Version::warn('request.params is deprecated. Use request.options to add default request options.');
30280 $request->getParams()->overwriteWith($params);
30281 }
30282
30283 if ($this->userAgent && !$request->hasHeader('User-Agent')) {
30284 $request->setHeader('User-Agent', $this->userAgent);
30285 }
30286
30287 if ($defaults = $this->config[self::REQUEST_OPTIONS]) {
30288 $this->requestFactory->applyOptions($request, $defaults, RequestFactoryInterface::OPTIONS_AS_DEFAULTS);
30289 }
30290
30291 if ($options) {
30292 $this->requestFactory->applyOptions($request, $options);
30293 }
30294
30295 $this->dispatch('client.create_request', array('client' => $this, 'request' => $request));
30296
30297 return $request;
30298 }
30299
30300 /**
30301 * Initializes SSL settings
30302 */
30303 protected function initSsl()
30304 {
30305 if ('system' == ($authority = $this->config[self::SSL_CERT_AUTHORITY])) {
30306 return;
30307 }
30308
30309 if ($authority === null) {
30310 $authority = true;
30311 }
30312
30313 if ($authority === true && substr(__FILE__, 0, 7) == 'phar://') {
30314 $authority = $this->preparePharCacert();
30315 $that = $this;
30316 $this->getEventDispatcher()->addListener('request.before_send', function ($event) use ($authority, $that) {
30317 if ($authority == $event['request']->getCurlOptions()->get(CURLOPT_CAINFO)) {
30318 $that->preparePharCacert(false);
30319 }
30320 });
30321 }
30322
30323 $this->setSslVerification($authority);
30324 }
30325
30326 /**
30327 * @deprecated
30328 */
30329 public function getDefaultHeaders()
30330 {
30331 Version::warn(__METHOD__ . ' is deprecated. Use the request.options array to retrieve default request options');
30332 return $this->defaultHeaders;
30333 }
30334
30335 /**
30336 * @deprecated
30337 */
30338 public function setDefaultHeaders($headers)
30339 {
30340 Version::warn(__METHOD__ . ' is deprecated. Use the request.options array to specify default request options');
30341 if ($headers instanceof Collection) {
30342 $this->defaultHeaders = $headers;
30343 } elseif (is_array($headers)) {
30344 $this->defaultHeaders = new Collection($headers);
30345 } else {
30346 throw new InvalidArgumentException('Headers must be an array or Collection');
30347 }
30348
30349 return $this;
30350 }
30351 }
30352 <?php
30353
30354 namespace Guzzle\Http\Message;
30355
30356 use Guzzle\Common\Version;
30357 use Guzzle\Common\Event;
30358 use Guzzle\Common\Collection;
30359 use Guzzle\Common\Exception\RuntimeException;
30360 use Guzzle\Common\Exception\InvalidArgumentException;
30361 use Guzzle\Http\Exception\RequestException;
30362 use Guzzle\Http\Exception\BadResponseException;
30363 use Guzzle\Http\ClientInterface;
30364 use Guzzle\Http\EntityBody;
30365 use Guzzle\Http\EntityBodyInterface;
30366 use Guzzle\Http\Message\Header\HeaderInterface;
30367 use Guzzle\Http\Url;
30368 use Guzzle\Parser\ParserRegistry;
30369 use Symfony\Component\EventDispatcher\EventDispatcherInterface;
30370 use Symfony\Component\EventDispatcher\EventDispatcher;
30371 use Symfony\Component\EventDispatcher\EventSubscriberInterface;
30372
30373 /**
30374 * HTTP request class to send requests
30375 */
30376 class Request extends AbstractMessage implements RequestInterface
30377 {
30378 /** @var EventDispatcherInterface */
30379 protected $eventDispatcher;
30380
30381 /** @var Url HTTP Url */
30382 protected $url;
30383
30384 /** @var string HTTP method (GET, PUT, POST, DELETE, HEAD, OPTIONS, TRACE) */
30385 protected $method;
30386
30387 /** @var ClientInterface */
30388 protected $client;
30389
30390 /** @var Response Response of the request */
30391 protected $response;
30392
30393 /** @var EntityBodyInterface Response body */
30394 protected $responseBody;
30395
30396 /** @var string State of the request object */
30397 protected $state;
30398
30399 /** @var string Authentication username */
30400 protected $username;
30401
30402 /** @var string Auth password */
30403 protected $password;
30404
30405 /** @var Collection cURL specific transfer options */
30406 protected $curlOptions;
30407
30408 /** @var bool */
30409 protected $isRedirect = false;
30410
30411 public static function getAllEvents()
30412 {
30413 return array(
30414 // Called when receiving or uploading data through cURL
30415 'curl.callback.read', 'curl.callback.write', 'curl.callback.progress',
30416 // Cloning a request
30417 'request.clone',
30418 // About to send the request, sent request, completed transaction
30419 'request.before_send', 'request.sent', 'request.complete',
30420 // A request received a successful response
30421 'request.success',
30422 // A request received an unsuccessful response
30423 'request.error',
30424 // An exception is being thrown because of an unsuccessful response
30425 'request.exception',
30426 // Received response status line
30427 'request.receive.status_line'
30428 );
30429 }
30430
30431 /**
30432 * @param string $method HTTP method
30433 * @param string|Url $url HTTP URL to connect to. The URI scheme, host header, and URI are parsed from the
30434 * full URL. If query string parameters are present they will be parsed as well.
30435 * @param array|Collection $headers HTTP headers
30436 */
30437 public function __construct($method, $url, $headers = array())
30438 {
30439 parent::__construct();
30440 $this->method = strtoupper($method);
30441 $this->curlOptions = new Collection();
30442 $this->setUrl($url);
30443
30444 if ($headers) {
30445 // Special handling for multi-value headers
30446 foreach ($headers as $key => $value) {
30447 // Deal with collisions with Host and Authorization
30448 if ($key == 'host' || $key == 'Host') {
30449 $this->setHeader($key, $value);
30450 } elseif ($value instanceof HeaderInterface) {
30451 $this->addHeader($key, $value);
30452 } else {
30453 foreach ((array) $value as $v) {
30454 $this->addHeader($key, $v);
30455 }
30456 }
30457 }
30458 }
30459
30460 $this->setState(self::STATE_NEW);
30461 }
30462
30463 public function __clone()
30464 {
30465 if ($this->eventDispatcher) {
30466 $this->eventDispatcher = clone $this->eventDispatcher;
30467 }
30468 $this->curlOptions = clone $this->curlOptions;
30469 $this->params = clone $this->params;
30470 $this->url = clone $this->url;
30471 $this->response = $this->responseBody = null;
30472 $this->headers = clone $this->headers;
30473
30474 $this->setState(RequestInterface::STATE_NEW);
30475 $this->dispatch('request.clone', array('request' => $this));
30476 }
30477
30478 /**
30479 * Get the HTTP request as a string
30480 *
30481 * @return string
30482 */
30483 public function __toString()
30484 {
30485 return $this->getRawHeaders() . "\r\n\r\n";
30486 }
30487
30488 /**
30489 * Default method that will throw exceptions if an unsuccessful response is received.
30490 *
30491 * @param Event $event Received
30492 * @throws BadResponseException if the response is not successful
30493 */
30494 public static function onRequestError(Event $event)
30495 {
30496 $e = BadResponseException::factory($event['request'], $event['response']);
30497 $event['request']->setState(self::STATE_ERROR, array('exception' => $e) + $event->toArray());
30498 throw $e;
30499 }
30500
30501 public function setClient(ClientInterface $client)
30502 {
30503 $this->client = $client;
30504
30505 return $this;
30506 }
30507
30508 public function getClient()
30509 {
30510 return $this->client;
30511 }
30512
30513 public function getRawHeaders()
30514 {
30515 $protocolVersion = $this->protocolVersion ?: '1.1';
30516
30517 return trim($this->method . ' ' . $this->getResource()) . ' '
30518 . strtoupper(str_replace('https', 'http', $this->url->getScheme()))
30519 . '/' . $protocolVersion . "\r\n" . implode("\r\n", $this->getHeaderLines());
30520 }
30521
30522 public function setUrl($url)
30523 {
30524 if ($url instanceof Url) {
30525 $this->url = $url;
30526 } else {
30527 $this->url = Url::factory($url);
30528 }
30529
30530 // Update the port and host header
30531 $this->setPort($this->url->getPort());
30532
30533 if ($this->url->getUsername() || $this->url->getPassword()) {
30534 $this->setAuth($this->url->getUsername(), $this->url->getPassword());
30535 // Remove the auth info from the URL
30536 $this->url->setUsername(null);
30537 $this->url->setPassword(null);
30538 }
30539
30540 return $this;
30541 }
30542
30543 public function send()
30544 {
30545 if (!$this->client) {
30546 throw new RuntimeException('A client must be set on the request');
30547 }
30548
30549 return $this->client->send($this);
30550 }
30551
30552 public function getResponse()
30553 {
30554 return $this->response;
30555 }
30556
30557 public function getQuery($asString = false)
30558 {
30559 return $asString
30560 ? (string) $this->url->getQuery()
30561 : $this->url->getQuery();
30562 }
30563
30564 public function getMethod()
30565 {
30566 return $this->method;
30567 }
30568
30569 public function getScheme()
30570 {
30571 return $this->url->getScheme();
30572 }
30573
30574 public function setScheme($scheme)
30575 {
30576 $this->url->setScheme($scheme);
30577
30578 return $this;
30579 }
30580
30581 public function getHost()
30582 {
30583 return $this->url->getHost();
30584 }
30585
30586 public function setHost($host)
30587 {
30588 $this->url->setHost($host);
30589 $this->setPort($this->url->getPort());
30590
30591 return $this;
30592 }
30593
30594 public function getProtocolVersion()
30595 {
30596 return $this->protocolVersion;
30597 }
30598
30599 public function setProtocolVersion($protocol)
30600 {
30601 $this->protocolVersion = $protocol;
30602
30603 return $this;
30604 }
30605
30606 public function getPath()
30607 {
30608 return '/' . ltrim($this->url->getPath(), '/');
30609 }
30610
30611 public function setPath($path)
30612 {
30613 $this->url->setPath($path);
30614
30615 return $this;
30616 }
30617
30618 public function getPort()
30619 {
30620 return $this->url->getPort();
30621 }
30622
30623 public function setPort($port)
30624 {
30625 $this->url->setPort($port);
30626
30627 // Include the port in the Host header if it is not the default port for the scheme of the URL
30628 $scheme = $this->url->getScheme();
30629 if (($scheme == 'http' && $port != 80) || ($scheme == 'https' && $port != 443)) {
30630 $this->headers['host'] = $this->headerFactory->createHeader('Host', $this->url->getHost() . ':' . $port);
30631 } else {
30632 $this->headers['host'] = $this->headerFactory->createHeader('Host', $this->url->getHost());
30633 }
30634
30635 return $this;
30636 }
30637
30638 public function getUsername()
30639 {
30640 return $this->username;
30641 }
30642
30643 public function getPassword()
30644 {
30645 return $this->password;
30646 }
30647
30648 public function setAuth($user, $password = '', $scheme = CURLAUTH_BASIC)
30649 {
30650 static $authMap = array(
30651 'basic' => CURLAUTH_BASIC,
30652 'digest' => CURLAUTH_DIGEST,
30653 'ntlm' => CURLAUTH_NTLM,
30654 'any' => CURLAUTH_ANY
30655 );
30656
30657 // If we got false or null, disable authentication
30658 if (!$user) {
30659 $this->password = $this->username = null;
30660 $this->removeHeader('Authorization');
30661 $this->getCurlOptions()->remove(CURLOPT_HTTPAUTH);
30662 return $this;
30663 }
30664
30665 if (!is_numeric($scheme)) {
30666 $scheme = strtolower($scheme);
30667 if (!isset($authMap[$scheme])) {
30668 throw new InvalidArgumentException($scheme . ' is not a valid authentication type');
30669 }
30670 $scheme = $authMap[$scheme];
30671 }
30672
30673 $this->username = $user;
30674 $this->password = $password;
30675
30676 // Bypass CURL when using basic auth to promote connection reuse
30677 if ($scheme == CURLAUTH_BASIC) {
30678 $this->getCurlOptions()->remove(CURLOPT_HTTPAUTH);
30679 $this->setHeader('Authorization', 'Basic ' . base64_encode($this->username . ':' . $this->password));
30680 } else {
30681 $this->getCurlOptions()
30682 ->set(CURLOPT_HTTPAUTH, $scheme)
30683 ->set(CURLOPT_USERPWD, $this->username . ':' . $this->password);
30684 }
30685
30686 return $this;
30687 }
30688
30689 public function getResource()
30690 {
30691 $resource = $this->getPath();
30692 if ($query = (string) $this->url->getQuery()) {
30693 $resource .= '?' . $query;
30694 }
30695
30696 return $resource;
30697 }
30698
30699 public function getUrl($asObject = false)
30700 {
30701 return $asObject ? clone $this->url : (string) $this->url;
30702 }
30703
30704 public function getState()
30705 {
30706 return $this->state;
30707 }
30708
30709 public function setState($state, array $context = array())
30710 {
30711 $oldState = $this->state;
30712 $this->state = $state;
30713
30714 switch ($state) {
30715 case self::STATE_NEW:
30716 $this->response = null;
30717 break;
30718 case self::STATE_TRANSFER:
30719 if ($oldState !== $state) {
30720 // Fix Content-Length and Transfer-Encoding collisions
30721 if ($this->hasHeader('Transfer-Encoding') && $this->hasHeader('Content-Length')) {
30722 $this->removeHeader('Transfer-Encoding');
30723 }
30724 $this->dispatch('request.before_send', array('request' => $this));
30725 }
30726 break;
30727 case self::STATE_COMPLETE:
30728 if ($oldState !== $state) {
30729 $this->processResponse($context);
30730 $this->responseBody = null;
30731 }
30732 break;
30733 case self::STATE_ERROR:
30734 if (isset($context['exception'])) {
30735 $this->dispatch('request.exception', array(
30736 'request' => $this,
30737 'response' => isset($context['response']) ? $context['response'] : $this->response,
30738 'exception' => isset($context['exception']) ? $context['exception'] : null
30739 ));
30740 }
30741 }
30742
30743 return $this->state;
30744 }
30745
30746 public function getCurlOptions()
30747 {
30748 return $this->curlOptions;
30749 }
30750
30751 public function startResponse(Response $response)
30752 {
30753 $this->state = self::STATE_TRANSFER;
30754 $response->setEffectiveUrl((string) $this->getUrl());
30755 $this->response = $response;
30756
30757 return $this;
30758 }
30759
30760 public function setResponse(Response $response, $queued = false)
30761 {
30762 $response->setEffectiveUrl((string) $this->url);
30763
30764 if ($queued) {
30765 $ed = $this->getEventDispatcher();
30766 $ed->addListener('request.before_send', $f = function ($e) use ($response, &$f, $ed) {
30767 $e['request']->setResponse($response);
30768 $ed->removeListener('request.before_send', $f);
30769 }, -9999);
30770 } else {
30771 $this->response = $response;
30772 // If a specific response body is specified, then use it instead of the response's body
30773 if ($this->responseBody && !$this->responseBody->getCustomData('default') && !$response->isRedirect()) {
30774 $this->getResponseBody()->write((string) $this->response->getBody());
30775 } else {
30776 $this->responseBody = $this->response->getBody();
30777 }
30778 $this->setState(self::STATE_COMPLETE);
30779 }
30780
30781 return $this;
30782 }
30783
30784 public function setResponseBody($body)
30785 {
30786 // Attempt to open a file for writing if a string was passed
30787 if (is_string($body)) {
30788 // @codeCoverageIgnoreStart
30789 if (!($body = fopen($body, 'w+'))) {
30790 throw new InvalidArgumentException('Could not open ' . $body . ' for writing');
30791 }
30792 // @codeCoverageIgnoreEnd
30793 }
30794
30795 $this->responseBody = EntityBody::factory($body);
30796
30797 return $this;
30798 }
30799
30800 public function getResponseBody()
30801 {
30802 if ($this->responseBody === null) {
30803 $this->responseBody = EntityBody::factory()->setCustomData('default', true);
30804 }
30805
30806 return $this->responseBody;
30807 }
30808
30809 /**
30810 * Determine if the response body is repeatable (readable + seekable)
30811 *
30812 * @return bool
30813 * @deprecated Use getResponseBody()->isSeekable()
30814 * @codeCoverageIgnore
30815 */
30816 public function isResponseBodyRepeatable()
30817 {
30818 Version::warn(__METHOD__ . ' is deprecated. Use $request->getResponseBody()->isRepeatable()');
30819 return !$this->responseBody ? true : $this->responseBody->isRepeatable();
30820 }
30821
30822 public function getCookies()
30823 {
30824 if ($cookie = $this->getHeader('Cookie')) {
30825 $data = ParserRegistry::getInstance()->getParser('cookie')->parseCookie($cookie);
30826 return $data['cookies'];
30827 }
30828
30829 return array();
30830 }
30831
30832 public function getCookie($name)
30833 {
30834 $cookies = $this->getCookies();
30835
30836 return isset($cookies[$name]) ? $cookies[$name] : null;
30837 }
30838
30839 public function addCookie($name, $value)
30840 {
30841 if (!$this->hasHeader('Cookie')) {
30842 $this->setHeader('Cookie', "{$name}={$value}");
30843 } else {
30844 $this->getHeader('Cookie')->add("{$name}={$value}");
30845 }
30846
30847 // Always use semicolons to separate multiple cookie headers
30848 $this->getHeader('Cookie')->setGlue(';');
30849
30850 return $this;
30851 }
30852
30853 public function removeCookie($name)
30854 {
30855 if ($cookie = $this->getHeader('Cookie')) {
30856 foreach ($cookie as $cookieValue) {
30857 if (strpos($cookieValue, $name . '=') === 0) {
30858 $cookie->removeValue($cookieValue);
30859 }
30860 }
30861 }
30862
30863 return $this;
30864 }
30865
30866 public function setEventDispatcher(EventDispatcherInterface $eventDispatcher)
30867 {
30868 $this->eventDispatcher = $eventDispatcher;
30869 $this->eventDispatcher->addListener('request.error', array(__CLASS__, 'onRequestError'), -255);
30870
30871 return $this;
30872 }
30873
30874 public function getEventDispatcher()
30875 {
30876 if (!$this->eventDispatcher) {
30877 $this->setEventDispatcher(new EventDispatcher());
30878 }
30879
30880 return $this->eventDispatcher;
30881 }
30882
30883 public function dispatch($eventName, array $context = array())
30884 {
30885 $context['request'] = $this;
30886
30887 return $this->getEventDispatcher()->dispatch($eventName, new Event($context));
30888 }
30889
30890 public function addSubscriber(EventSubscriberInterface $subscriber)
30891 {
30892 $this->getEventDispatcher()->addSubscriber($subscriber);
30893
30894 return $this;
30895 }
30896
30897 /**
30898 * Get an array containing the request and response for event notifications
30899 *
30900 * @return array
30901 */
30902 protected function getEventArray()
30903 {
30904 return array(
30905 'request' => $this,
30906 'response' => $this->response
30907 );
30908 }
30909
30910 /**
30911 * Process a received response
30912 *
30913 * @param array $context Contextual information
30914 * @throws RequestException|BadResponseException on unsuccessful responses
30915 */
30916 protected function processResponse(array $context = array())
30917 {
30918 if (!$this->response) {
30919 // If no response, then processResponse shouldn't have been called
30920 $e = new RequestException('Error completing request');
30921 $e->setRequest($this);
30922 throw $e;
30923 }
30924
30925 $this->state = self::STATE_COMPLETE;
30926
30927 // A request was sent, but we don't know if we'll send more or if the final response will be successful
30928 $this->dispatch('request.sent', $this->getEventArray() + $context);
30929
30930 // Some response processors will remove the response or reset the state (example: ExponentialBackoffPlugin)
30931 if ($this->state == RequestInterface::STATE_COMPLETE) {
30932
30933 // The request completed, so the HTTP transaction is complete
30934 $this->dispatch('request.complete', $this->getEventArray());
30935
30936 // If the response is bad, allow listeners to modify it or throw exceptions. You can change the response by
30937 // modifying the Event object in your listeners or calling setResponse() on the request
30938 if ($this->response->isError()) {
30939 $event = new Event($this->getEventArray());
30940 $this->getEventDispatcher()->dispatch('request.error', $event);
30941 // Allow events of request.error to quietly change the response
30942 if ($event['response'] !== $this->response) {
30943 $this->response = $event['response'];
30944 }
30945 }
30946
30947 // If a successful response was received, dispatch an event
30948 if ($this->response->isSuccessful()) {
30949 $this->dispatch('request.success', $this->getEventArray());
30950 }
30951 }
30952 }
30953
30954 /**
30955 * @deprecated Use Guzzle\Plugin\Cache\DefaultCanCacheStrategy
30956 * @codeCoverageIgnore
30957 */
30958 public function canCache()
30959 {
30960 Version::warn(__METHOD__ . ' is deprecated. Use Guzzle\Plugin\Cache\DefaultCanCacheStrategy.');
30961 if (class_exists('Guzzle\Plugin\Cache\DefaultCanCacheStrategy')) {
30962 $canCache = new \Guzzle\Plugin\Cache\DefaultCanCacheStrategy();
30963 return $canCache->canCacheRequest($this);
30964 } else {
30965 return false;
30966 }
30967 }
30968
30969 /**
30970 * @deprecated Use the history plugin (not emitting a warning as this is built-into the RedirectPlugin for now)
30971 * @codeCoverageIgnore
30972 */
30973 public function setIsRedirect($isRedirect)
30974 {
30975 $this->isRedirect = $isRedirect;
30976
30977 return $this;
30978 }
30979
30980 /**
30981 * @deprecated Use the history plugin
30982 * @codeCoverageIgnore
30983 */
30984 public function isRedirect()
30985 {
30986 Version::warn(__METHOD__ . ' is deprecated. Use the HistoryPlugin to track this.');
30987 return $this->isRedirect;
30988 }
30989 }
30990 <?php
30991
30992 namespace Guzzle\Http\Message;
30993
30994 use Guzzle\Common\Collection;
30995 use Guzzle\Common\HasDispatcherInterface;
30996 use Guzzle\Http\Exception\RequestException;
30997 use Guzzle\Http\ClientInterface;
30998 use Guzzle\Http\EntityBodyInterface;
30999 use Guzzle\Http\Url;
31000 use Guzzle\Http\QueryString;
31001
31002 /**
31003 * Generic HTTP request interface
31004 */
31005 interface RequestInterface extends MessageInterface, HasDispatcherInterface
31006 {
31007 const STATE_NEW = 'new';
31008 const STATE_COMPLETE = 'complete';
31009 const STATE_TRANSFER = 'transfer';
31010 const STATE_ERROR = 'error';
31011
31012 const GET = 'GET';
31013 const PUT = 'PUT';
31014 const POST = 'POST';
31015 const DELETE = 'DELETE';
31016 const HEAD = 'HEAD';
31017 const CONNECT = 'CONNECT';
31018 const OPTIONS = 'OPTIONS';
31019 const TRACE = 'TRACE';
31020 const PATCH = 'PATCH';
31021
31022 /**
31023 * @return string
31024 */
31025 public function __toString();
31026
31027 /**
31028 * Send the request
31029 *
31030 * @return Response
31031 * @throws RequestException on a request error
31032 */
31033 public function send();
31034
31035 /**
31036 * Set the client used to transport the request
31037 *
31038 * @param ClientInterface $client
31039 *
31040 * @return self
31041 */
31042 public function setClient(ClientInterface $client);
31043
31044 /**
31045 * Get the client used to transport the request
31046 *
31047 * @return ClientInterface $client
31048 */
31049 public function getClient();
31050
31051 /**
31052 * Set the URL of the request
31053 *
31054 * @param string $url|Url Full URL to set including query string
31055 *
31056 * @return self
31057 */
31058 public function setUrl($url);
31059
31060 /**
31061 * Get the full URL of the request (e.g. 'http://www.guzzle-project.com/')
31062 *
31063 * @param bool $asObject Set to TRUE to retrieve the URL as a clone of the URL object owned by the request.
31064 *
31065 * @return string|Url
31066 */
31067 public function getUrl($asObject = false);
31068
31069 /**
31070 * Get the resource part of the the request, including the path, query string, and fragment
31071 *
31072 * @return string
31073 */
31074 public function getResource();
31075
31076 /**
31077 * Get the collection of key value pairs that will be used as the query string in the request
31078 *
31079 * @return QueryString
31080 */
31081 public function getQuery();
31082
31083 /**
31084 * Get the HTTP method of the request
31085 *
31086 * @return string
31087 */
31088 public function getMethod();
31089
31090 /**
31091 * Get the URI scheme of the request (http, https, ftp, etc)
31092 *
31093 * @return string
31094 */
31095 public function getScheme();
31096
31097 /**
31098 * Set the URI scheme of the request (http, https, ftp, etc)
31099 *
31100 * @param string $scheme Scheme to set
31101 *
31102 * @return self
31103 */
31104 public function setScheme($scheme);
31105
31106 /**
31107 * Get the host of the request
31108 *
31109 * @return string
31110 */
31111 public function getHost();
31112
31113 /**
31114 * Set the host of the request. Including a port in the host will modify the port of the request.
31115 *
31116 * @param string $host Host to set (e.g. www.yahoo.com, www.yahoo.com:80)
31117 *
31118 * @return self
31119 */
31120 public function setHost($host);
31121
31122 /**
31123 * Get the path of the request (e.g. '/', '/index.html')
31124 *
31125 * @return string
31126 */
31127 public function getPath();
31128
31129 /**
31130 * Set the path of the request (e.g. '/', '/index.html')
31131 *
31132 * @param string|array $path Path to set or array of segments to implode
31133 *
31134 * @return self
31135 */
31136 public function setPath($path);
31137
31138 /**
31139 * Get the port that the request will be sent on if it has been set
31140 *
31141 * @return int|null
31142 */
31143 public function getPort();
31144
31145 /**
31146 * Set the port that the request will be sent on
31147 *
31148 * @param int $port Port number to set
31149 *
31150 * @return self
31151 */
31152 public function setPort($port);
31153
31154 /**
31155 * Get the username to pass in the URL if set
31156 *
31157 * @return string|null
31158 */
31159 public function getUsername();
31160
31161 /**
31162 * Get the password to pass in the URL if set
31163 *
31164 * @return string|null
31165 */
31166 public function getPassword();
31167
31168 /**
31169 * Set HTTP authorization parameters
31170 *
31171 * @param string|bool $user User name or false disable authentication
31172 * @param string $password Password
31173 * @param string $scheme Authentication scheme ('Basic', 'Digest', or a CURLAUTH_* constant (deprecated))
31174 *
31175 * @return self
31176 * @link http://www.ietf.org/rfc/rfc2617.txt
31177 * @link http://php.net/manual/en/function.curl-setopt.php See the available options for CURLOPT_HTTPAUTH
31178 * @throws RequestException
31179 */
31180 public function setAuth($user, $password = '', $scheme = 'Basic');
31181
31182 /**
31183 * Get the HTTP protocol version of the request
31184 *
31185 * @return string
31186 */
31187 public function getProtocolVersion();
31188
31189 /**
31190 * Set the HTTP protocol version of the request (e.g. 1.1 or 1.0)
31191 *
31192 * @param string $protocol HTTP protocol version to use with the request
31193 *
31194 * @return self
31195 */
31196 public function setProtocolVersion($protocol);
31197
31198 /**
31199 * Get the previously received {@see Response} or NULL if the request has not been sent
31200 *
31201 * @return Response|null
31202 */
31203 public function getResponse();
31204
31205 /**
31206 * Manually set a response for the request.
31207 *
31208 * This method is useful for specifying a mock response for the request or setting the response using a cache.
31209 * Manually setting a response will bypass the actual sending of a request.
31210 *
31211 * @param Response $response Response object to set
31212 * @param bool $queued Set to TRUE to keep the request in a state of not having been sent, but queue the
31213 * response for send()
31214 *
31215 * @return self Returns a reference to the object.
31216 */
31217 public function setResponse(Response $response, $queued = false);
31218
31219 /**
31220 * The start of a response has been received for a request and the request is still in progress
31221 *
31222 * @param Response $response Response that has been received so far
31223 *
31224 * @return self
31225 */
31226 public function startResponse(Response $response);
31227
31228 /**
31229 * Set the EntityBody that will hold a successful response message's entity body.
31230 *
31231 * This method should be invoked when you need to send the response's entity body somewhere other than the normal
31232 * php://temp buffer. For example, you can send the entity body to a socket, file, or some other custom stream.
31233 *
31234 * @param EntityBodyInterface|string|resource $body Response body object. Pass a string to attempt to store the
31235 * response body in a local file.
31236 * @return Request
31237 */
31238 public function setResponseBody($body);
31239
31240 /**
31241 * Get the EntityBody that will hold the resulting response message's entity body. This response body will only
31242 * be used for successful responses. Intermediate responses (e.g. redirects) will not use the targeted response
31243 * body.
31244 *
31245 * @return EntityBodyInterface
31246 */
31247 public function getResponseBody();
31248
31249 /**
31250 * Get the state of the request. One of 'complete', 'transfer', 'new', 'error'
31251 *
31252 * @return string
31253 */
31254 public function getState();
31255
31256 /**
31257 * Set the state of the request
31258 *
31259 * @param string $state State of the request ('complete', 'transfer', 'new', 'error')
31260 * @param array $context Contextual information about the state change
31261 *
31262 * @return string Returns the current state of the request (which may have changed due to events being fired)
31263 */
31264 public function setState($state, array $context = array());
31265
31266 /**
31267 * Get the cURL options that will be applied when the cURL handle is created
31268 *
31269 * @return Collection
31270 */
31271 public function getCurlOptions();
31272
31273 /**
31274 * Get an array of Cookies
31275 *
31276 * @return array
31277 */
31278 public function getCookies();
31279
31280 /**
31281 * Get a cookie value by name
31282 *
31283 * @param string $name Cookie to retrieve
31284 *
31285 * @return null|string
31286 */
31287 public function getCookie($name);
31288
31289 /**
31290 * Add a Cookie value by name to the Cookie header
31291 *
31292 * @param string $name Name of the cookie to add
31293 * @param string $value Value to set
31294 *
31295 * @return self
31296 */
31297 public function addCookie($name, $value);
31298
31299 /**
31300 * Remove a specific cookie value by name
31301 *
31302 * @param string $name Cookie to remove by name
31303 *
31304 * @return self
31305 */
31306 public function removeCookie($name);
31307 }
31308 <?php
31309
31310 namespace Guzzle\Http\Message;
31311
31312 use Guzzle\Common\Version;
31313 use Guzzle\Common\Collection;
31314 use Guzzle\Http\Message\Header\HeaderCollection;
31315 use Guzzle\Http\Message\Header\HeaderFactory;
31316 use Guzzle\Http\Message\Header\HeaderFactoryInterface;
31317 use Guzzle\Http\Message\Header\HeaderInterface;
31318
31319 /**
31320 * Abstract HTTP request/response message
31321 */
31322 abstract class AbstractMessage implements MessageInterface
31323 {
31324 /** @var array HTTP header collection */
31325 protected $headers;
31326
31327 /** @var HeaderFactoryInterface $headerFactory */
31328 protected $headerFactory;
31329
31330 /** @var Collection Custom message parameters that are extendable by plugins */
31331 protected $params;
31332
31333 /** @var string Message protocol */
31334 protected $protocol = 'HTTP';
31335
31336 /** @var string HTTP protocol version of the message */
31337 protected $protocolVersion = '1.1';
31338
31339 public function __construct()
31340 {
31341 $this->params = new Collection();
31342 $this->headerFactory = new HeaderFactory();
31343 $this->headers = new HeaderCollection();
31344 }
31345
31346 /**
31347 * Set the header factory to use to create headers
31348 *
31349 * @param HeaderFactoryInterface $factory
31350 *
31351 * @return self
31352 */
31353 public function setHeaderFactory(HeaderFactoryInterface $factory)
31354 {
31355 $this->headerFactory = $factory;
31356
31357 return $this;
31358 }
31359
31360 public function getParams()
31361 {
31362 return $this->params;
31363 }
31364
31365 public function addHeader($header, $value)
31366 {
31367 if (isset($this->headers[$header])) {
31368 $this->headers[$header]->add($value);
31369 } elseif ($value instanceof HeaderInterface) {
31370 $this->headers[$header] = $value;
31371 } else {
31372 $this->headers[$header] = $this->headerFactory->createHeader($header, $value);
31373 }
31374
31375 return $this;
31376 }
31377
31378 public function addHeaders(array $headers)
31379 {
31380 foreach ($headers as $key => $value) {
31381 $this->addHeader($key, $value);
31382 }
31383
31384 return $this;
31385 }
31386
31387 public function getHeader($header)
31388 {
31389 return $this->headers[$header];
31390 }
31391
31392 public function getHeaders()
31393 {
31394 return $this->headers;
31395 }
31396
31397 public function getHeaderLines()
31398 {
31399 $headers = array();
31400 foreach ($this->headers as $value) {
31401 $headers[] = $value->getName() . ': ' . $value;
31402 }
31403
31404 return $headers;
31405 }
31406
31407 public function setHeader($header, $value)
31408 {
31409 unset($this->headers[$header]);
31410 $this->addHeader($header, $value);
31411
31412 return $this;
31413 }
31414
31415 public function setHeaders(array $headers)
31416 {
31417 $this->headers->clear();
31418 foreach ($headers as $key => $value) {
31419 $this->addHeader($key, $value);
31420 }
31421
31422 return $this;
31423 }
31424
31425 public function hasHeader($header)
31426 {
31427 return isset($this->headers[$header]);
31428 }
31429
31430 public function removeHeader($header)
31431 {
31432 unset($this->headers[$header]);
31433
31434 return $this;
31435 }
31436
31437 /**
31438 * @deprecated Use $message->getHeader()->parseParams()
31439 * @codeCoverageIgnore
31440 */
31441 public function getTokenizedHeader($header, $token = ';')
31442 {
31443 Version::warn(__METHOD__ . ' is deprecated. Use $message->getHeader()->parseParams()');
31444 if ($this->hasHeader($header)) {
31445 $data = new Collection();
31446 foreach ($this->getHeader($header)->parseParams() as $values) {
31447 foreach ($values as $key => $value) {
31448 if ($value === '') {
31449 $data->set($data->count(), $key);
31450 } else {
31451 $data->add($key, $value);
31452 }
31453 }
31454 }
31455 return $data;
31456 }
31457 }
31458
31459 /**
31460 * @deprecated
31461 * @codeCoverageIgnore
31462 */
31463 public function setTokenizedHeader($header, $data, $token = ';')
31464 {
31465 Version::warn(__METHOD__ . ' is deprecated.');
31466 return $this;
31467 }
31468
31469 /**
31470 * @deprecated
31471 * @codeCoverageIgnore
31472 */
31473 public function getCacheControlDirective($directive)
31474 {
31475 Version::warn(__METHOD__ . ' is deprecated. Use $message->getHeader(\'Cache-Control\')->getDirective()');
31476 if (!($header = $this->getHeader('Cache-Control'))) {
31477 return null;
31478 }
31479
31480 return $header->getDirective($directive);
31481 }
31482
31483 /**
31484 * @deprecated
31485 * @codeCoverageIgnore
31486 */
31487 public function hasCacheControlDirective($directive)
31488 {
31489 Version::warn(__METHOD__ . ' is deprecated. Use $message->getHeader(\'Cache-Control\')->hasDirective()');
31490 if ($header = $this->getHeader('Cache-Control')) {
31491 return $header->hasDirective($directive);
31492 } else {
31493 return false;
31494 }
31495 }
31496
31497 /**
31498 * @deprecated
31499 * @codeCoverageIgnore
31500 */
31501 public function addCacheControlDirective($directive, $value = true)
31502 {
31503 Version::warn(__METHOD__ . ' is deprecated. Use $message->getHeader(\'Cache-Control\')->addDirective()');
31504 if (!($header = $this->getHeader('Cache-Control'))) {
31505 $this->addHeader('Cache-Control', '');
31506 $header = $this->getHeader('Cache-Control');
31507 }
31508
31509 $header->addDirective($directive, $value);
31510
31511 return $this;
31512 }
31513
31514 /**
31515 * @deprecated
31516 * @codeCoverageIgnore
31517 */
31518 public function removeCacheControlDirective($directive)
31519 {
31520 Version::warn(__METHOD__ . ' is deprecated. Use $message->getHeader(\'Cache-Control\')->removeDirective()');
31521 if ($header = $this->getHeader('Cache-Control')) {
31522 $header->removeDirective($directive);
31523 }
31524
31525 return $this;
31526 }
31527 }
31528 <?php
31529
31530 namespace Guzzle\Http\Message;
31531
31532 use Guzzle\Http\EntityBody;
31533 use Guzzle\Http\EntityBodyInterface;
31534 use Guzzle\Http\QueryString;
31535 use Guzzle\Http\RedirectPlugin;
31536 use Guzzle\Http\Exception\RequestException;
31537 use Guzzle\Http\Mimetypes;
31538
31539 /**
31540 * HTTP request that sends an entity-body in the request message (POST, PUT, PATCH, DELETE)
31541 */
31542 class EntityEnclosingRequest extends Request implements EntityEnclosingRequestInterface
31543 {
31544 /** @var int When the size of the body is greater than 1MB, then send Expect: 100-Continue */
31545 protected $expectCutoff = 1048576;
31546
31547 /** @var EntityBodyInterface $body Body of the request */
31548 protected $body;
31549
31550 /** @var QueryString POST fields to use in the EntityBody */
31551 protected $postFields;
31552
31553 /** @var array POST files to send with the request */
31554 protected $postFiles = array();
31555
31556 public function __construct($method, $url, $headers = array())
31557 {
31558 $this->postFields = new QueryString();
31559 parent::__construct($method, $url, $headers);
31560 }
31561
31562 /**
31563 * @return string
31564 */
31565 public function __toString()
31566 {
31567 // Only attempt to include the POST data if it's only fields
31568 if (count($this->postFields) && empty($this->postFiles)) {
31569 return parent::__toString() . (string) $this->postFields;
31570 }
31571
31572 return parent::__toString() . $this->body;
31573 }
31574
31575 public function setState($state, array $context = array())
31576 {
31577 parent::setState($state, $context);
31578 if ($state == self::STATE_TRANSFER && !$this->body && !count($this->postFields) && !count($this->postFiles)) {
31579 $this->setHeader('Content-Length', 0)->removeHeader('Transfer-Encoding');
31580 }
31581
31582 return $this->state;
31583 }
31584
31585 public function setBody($body, $contentType = null)
31586 {
31587 $this->body = EntityBody::factory($body);
31588
31589 // Auto detect the Content-Type from the path of the request if possible
31590 if ($contentType === null && !$this->hasHeader('Content-Type')) {
31591 $contentType = $this->body->getContentType();
31592 }
31593
31594 if ($contentType) {
31595 $this->setHeader('Content-Type', $contentType);
31596 }
31597
31598 // Always add the Expect 100-Continue header if the body cannot be rewound. This helps with redirects.
31599 if (!$this->body->isSeekable() && $this->expectCutoff !== false) {
31600 $this->setHeader('Expect', '100-Continue');
31601 }
31602
31603 // Set the Content-Length header if it can be determined
31604 $size = $this->body->getContentLength();
31605 if ($size !== null && $size !== false) {
31606 $this->setHeader('Content-Length', $size);
31607 if ($size > $this->expectCutoff) {
31608 $this->setHeader('Expect', '100-Continue');
31609 }
31610 } elseif (!$this->hasHeader('Content-Length')) {
31611 if ('1.1' == $this->protocolVersion) {
31612 $this->setHeader('Transfer-Encoding', 'chunked');
31613 } else {
31614 throw new RequestException(
31615 'Cannot determine Content-Length and cannot use chunked Transfer-Encoding when using HTTP/1.0'
31616 );
31617 }
31618 }
31619
31620 return $this;
31621 }
31622
31623 public function getBody()
31624 {
31625 return $this->body;
31626 }
31627
31628 /**
31629 * Set the size that the entity body of the request must exceed before adding the Expect: 100-Continue header.
31630 *
31631 * @param int|bool $size Cutoff in bytes. Set to false to never send the expect header (even with non-seekable data)
31632 *
31633 * @return self
31634 */
31635 public function setExpectHeaderCutoff($size)
31636 {
31637 $this->expectCutoff = $size;
31638 if ($size === false || !$this->body) {
31639 $this->removeHeader('Expect');
31640 } elseif ($this->body && $this->body->getSize() && $this->body->getSize() > $size) {
31641 $this->setHeader('Expect', '100-Continue');
31642 }
31643
31644 return $this;
31645 }
31646
31647 public function configureRedirects($strict = false, $maxRedirects = 5)
31648 {
31649 $this->getParams()->set(RedirectPlugin::STRICT_REDIRECTS, $strict);
31650 if ($maxRedirects == 0) {
31651 $this->getParams()->set(RedirectPlugin::DISABLE, true);
31652 } else {
31653 $this->getParams()->set(RedirectPlugin::MAX_REDIRECTS, $maxRedirects);
31654 }
31655
31656 return $this;
31657 }
31658
31659 public function getPostField($field)
31660 {
31661 return $this->postFields->get($field);
31662 }
31663
31664 public function getPostFields()
31665 {
31666 return $this->postFields;
31667 }
31668
31669 public function setPostField($key, $value)
31670 {
31671 $this->postFields->set($key, $value);
31672 $this->processPostFields();
31673
31674 return $this;
31675 }
31676
31677 public function addPostFields($fields)
31678 {
31679 $this->postFields->merge($fields);
31680 $this->processPostFields();
31681
31682 return $this;
31683 }
31684
31685 public function removePostField($field)
31686 {
31687 $this->postFields->remove($field);
31688 $this->processPostFields();
31689
31690 return $this;
31691 }
31692
31693 public function getPostFiles()
31694 {
31695 return $this->postFiles;
31696 }
31697
31698 public function getPostFile($fieldName)
31699 {
31700 return isset($this->postFiles[$fieldName]) ? $this->postFiles[$fieldName] : null;
31701 }
31702
31703 public function removePostFile($fieldName)
31704 {
31705 unset($this->postFiles[$fieldName]);
31706 $this->processPostFields();
31707
31708 return $this;
31709 }
31710
31711 public function addPostFile($field, $filename = null, $contentType = null)
31712 {
31713 $data = null;
31714
31715 if ($field instanceof PostFileInterface) {
31716 $data = $field;
31717 } elseif (is_array($filename)) {
31718 // Allow multiple values to be set in a single key
31719 foreach ($filename as $file) {
31720 $this->addPostFile($field, $file, $contentType);
31721 }
31722 return $this;
31723 } elseif (!is_string($filename)) {
31724 throw new RequestException('The path to a file must be a string');
31725 } elseif (!empty($filename)) {
31726 // Adding an empty file will cause cURL to error out
31727 $data = new PostFile($field, $filename, $contentType);
31728 }
31729
31730 if ($data) {
31731 if (!isset($this->postFiles[$data->getFieldName()])) {
31732 $this->postFiles[$data->getFieldName()] = array($data);
31733 } else {
31734 $this->postFiles[$data->getFieldName()][] = $data;
31735 }
31736 $this->processPostFields();
31737 }
31738
31739 return $this;
31740 }
31741
31742 public function addPostFiles(array $files)
31743 {
31744 foreach ($files as $key => $file) {
31745 if ($file instanceof PostFileInterface) {
31746 $this->addPostFile($file, null, null, false);
31747 } elseif (is_string($file)) {
31748 // Convert non-associative array keys into 'file'
31749 if (is_numeric($key)) {
31750 $key = 'file';
31751 }
31752 $this->addPostFile($key, $file, null, false);
31753 } else {
31754 throw new RequestException('File must be a string or instance of PostFileInterface');
31755 }
31756 }
31757
31758 return $this;
31759 }
31760
31761 /**
31762 * Determine what type of request should be sent based on post fields
31763 */
31764 protected function processPostFields()
31765 {
31766 if (!$this->postFiles) {
31767 $this->removeHeader('Expect')->setHeader('Content-Type', self::URL_ENCODED);
31768 } else {
31769 $this->setHeader('Content-Type', self::MULTIPART);
31770 if ($this->expectCutoff !== false) {
31771 $this->setHeader('Expect', '100-Continue');
31772 }
31773 }
31774 }
31775 }
31776 <?php
31777
31778 namespace Guzzle\Http\Message;
31779
31780 use Guzzle\Common\Version;
31781 use Guzzle\Common\Exception\InvalidArgumentException;
31782 use Guzzle\Http\Mimetypes;
31783
31784 /**
31785 * POST file upload
31786 */
31787 class PostFile implements PostFileInterface
31788 {
31789 protected $fieldName;
31790 protected $contentType;
31791 protected $filename;
31792
31793 /**
31794 * @param string $fieldName Name of the field
31795 * @param string $filename Path to the file
31796 * @param string $contentType Content-Type of the upload
31797 */
31798 public function __construct($fieldName, $filename, $contentType = null)
31799 {
31800 $this->fieldName = $fieldName;
31801 $this->setFilename($filename);
31802 $this->contentType = $contentType ?: $this->guessContentType();
31803 }
31804
31805 public function setFieldName($name)
31806 {
31807 $this->fieldName = $name;
31808
31809 return $this;
31810 }
31811
31812 public function getFieldName()
31813 {
31814 return $this->fieldName;
31815 }
31816
31817 public function setFilename($filename)
31818 {
31819 // Remove leading @ symbol
31820 if (strpos($filename, '@') === 0) {
31821 $filename = substr($filename, 1);
31822 }
31823
31824 if (!is_readable($filename)) {
31825 throw new InvalidArgumentException("Unable to open {$filename} for reading");
31826 }
31827
31828 $this->filename = $filename;
31829
31830 return $this;
31831 }
31832
31833 public function getFilename()
31834 {
31835 return $this->filename;
31836 }
31837
31838 public function setContentType($type)
31839 {
31840 $this->contentType = $type;
31841
31842 return $this;
31843 }
31844
31845 public function getContentType()
31846 {
31847 return $this->contentType;
31848 }
31849
31850 public function getCurlValue()
31851 {
31852 // PHP 5.5 introduced a CurlFile object that deprecates the old @filename syntax
31853 // See: https://wiki.php.net/rfc/curl-file-upload
31854 if (function_exists('curl_file_create')) {
31855 return curl_file_create($this->filename, $this->contentType, basename($this->filename));
31856 }
31857
31858 // Use the old style if using an older version of PHP
31859 $value = "@{$this->filename};filename=" . basename($this->filename);
31860 if ($this->contentType) {
31861 $value .= ';type=' . $this->contentType;
31862 }
31863
31864 return $value;
31865 }
31866
31867 /**
31868 * @deprecated
31869 * @codeCoverageIgnore
31870 */
31871 public function getCurlString()
31872 {
31873 Version::warn(__METHOD__ . ' is deprecated. Use getCurlValue()');
31874 return $this->getCurlValue();
31875 }
31876
31877 /**
31878 * Determine the Content-Type of the file
31879 */
31880 protected function guessContentType()
31881 {
31882 return Mimetypes::getInstance()->fromFilename($this->filename) ?: 'application/octet-stream';
31883 }
31884 }
31885 <?php
31886
31887 namespace Guzzle\Http\Message;
31888
31889 use Guzzle\Common\Version;
31890 use Guzzle\Http\Message\Header\HeaderInterface;
31891
31892 /**
31893 * Represents a header and all of the values stored by that header
31894 */
31895 class Header implements HeaderInterface
31896 {
31897 protected $values = array();
31898 protected $header;
31899 protected $glue;
31900
31901 /**
31902 * @param string $header Name of the header
31903 * @param array|string $values Values of the header as an array or a scalar
31904 * @param string $glue Glue used to combine multiple values into a string
31905 */
31906 public function __construct($header, $values = array(), $glue = ',')
31907 {
31908 $this->header = trim($header);
31909 $this->glue = $glue;
31910
31911 foreach ((array) $values as $value) {
31912 foreach ((array) $value as $v) {
31913 $this->values[] = $v;
31914 }
31915 }
31916 }
31917
31918 public function __toString()
31919 {
31920 return implode($this->glue . ' ', $this->toArray());
31921 }
31922
31923 public function add($value)
31924 {
31925 $this->values[] = $value;
31926
31927 return $this;
31928 }
31929
31930 public function getName()
31931 {
31932 return $this->header;
31933 }
31934
31935 public function setName($name)
31936 {
31937 $this->header = $name;
31938
31939 return $this;
31940 }
31941
31942 public function setGlue($glue)
31943 {
31944 $this->glue = $glue;
31945
31946 return $this;
31947 }
31948
31949 public function getGlue()
31950 {
31951 return $this->glue;
31952 }
31953
31954 /**
31955 * Normalize the header to be a single header with an array of values.
31956 *
31957 * If any values of the header contains the glue string value (e.g. ","), then the value will be exploded into
31958 * multiple entries in the header.
31959 *
31960 * @return self
31961 */
31962 public function normalize()
31963 {
31964 $values = $this->toArray();
31965
31966 for ($i = 0, $total = count($values); $i < $total; $i++) {
31967 if (strpos($values[$i], $this->glue) !== false) {
31968 // Explode on glue when the glue is not inside of a comma
31969 foreach (preg_split('/' . preg_quote($this->glue) . '(?=([^"]*"[^"]*")*[^"]*$)/', $values[$i]) as $v) {
31970 $values[] = trim($v);
31971 }
31972 unset($values[$i]);
31973 }
31974 }
31975
31976 $this->values = array_values($values);
31977
31978 return $this;
31979 }
31980
31981 public function hasValue($searchValue)
31982 {
31983 return in_array($searchValue, $this->toArray());
31984 }
31985
31986 public function removeValue($searchValue)
31987 {
31988 $this->values = array_values(array_filter($this->values, function ($value) use ($searchValue) {
31989 return $value != $searchValue;
31990 }));
31991
31992 return $this;
31993 }
31994
31995 public function toArray()
31996 {
31997 return $this->values;
31998 }
31999
32000 public function count()
32001 {
32002 return count($this->toArray());
32003 }
32004
32005 public function getIterator()
32006 {
32007 return new \ArrayIterator($this->toArray());
32008 }
32009
32010 public function parseParams()
32011 {
32012 $params = $matches = array();
32013 $callback = array($this, 'trimHeader');
32014
32015 // Normalize the header into a single array and iterate over all values
32016 foreach ($this->normalize()->toArray() as $val) {
32017 $part = array();
32018 foreach (preg_split('/;(?=([^"]*"[^"]*")*[^"]*$)/', $val) as $kvp) {
32019 preg_match_all('/<[^>]+>|[^=]+/', $kvp, $matches);
32020 $pieces = array_map($callback, $matches[0]);
32021 $part[$pieces[0]] = isset($pieces[1]) ? $pieces[1] : '';
32022 }
32023 $params[] = $part;
32024 }
32025
32026 return $params;
32027 }
32028
32029 /**
32030 * @deprecated
32031 * @codeCoverageIgnore
32032 */
32033 public function hasExactHeader($header)
32034 {
32035 Version::warn(__METHOD__ . ' is deprecated');
32036 return $this->header == $header;
32037 }
32038
32039 /**
32040 * @deprecated
32041 * @codeCoverageIgnore
32042 */
32043 public function raw()
32044 {
32045 Version::warn(__METHOD__ . ' is deprecated. Use toArray()');
32046 return $this->toArray();
32047 }
32048
32049 /**
32050 * Trim a header by removing excess spaces and wrapping quotes
32051 *
32052 * @param $str
32053 *
32054 * @return string
32055 */
32056 protected function trimHeader($str)
32057 {
32058 static $trimmed = "\"' \n\t";
32059
32060 return trim($str, $trimmed);
32061 }
32062 }
32063 <?php
32064
32065 namespace Guzzle\Http\Message\Header;
32066
32067 use Guzzle\Http\Message\Header;
32068
32069 /**
32070 * Provides helpful functionality for link headers
32071 */
32072 class Link extends Header
32073 {
32074 /**
32075 * Add a link to the header
32076 *
32077 * @param string $url Link URL
32078 * @param string $rel Link rel
32079 * @param array $params Other link parameters
32080 *
32081 * @return self
32082 */
32083 public function addLink($url, $rel, array $params = array())
32084 {
32085 $values = array("<{$url}>", "rel=\"{$rel}\"");
32086
32087 foreach ($params as $k => $v) {
32088 $values[] = "{$k}=\"{$v}\"";
32089 }
32090
32091 return $this->add(implode('; ', $values));
32092 }
32093
32094 /**
32095 * Check if a specific link exists for a given rel attribute
32096 *
32097 * @param string $rel rel value
32098 *
32099 * @return bool
32100 */
32101 public function hasLink($rel)
32102 {
32103 return $this->getLink($rel) !== null;
32104 }
32105
32106 /**
32107 * Get a specific link for a given rel attribute
32108 *
32109 * @param string $rel Rel value
32110 *
32111 * @return array|null
32112 */
32113 public function getLink($rel)
32114 {
32115 foreach ($this->getLinks() as $link) {
32116 if (isset($link['rel']) && $link['rel'] == $rel) {
32117 return $link;
32118 }
32119 }
32120
32121 return null;
32122 }
32123
32124 /**
32125 * Get an associative array of links
32126 *
32127 * For example:
32128 * Link: <http:/.../front.jpeg>; rel=front; type="image/jpeg", <http://.../back.jpeg>; rel=back; type="image/jpeg"
32129 *
32130 * <code>
32131 * var_export($response->getLinks());
32132 * array(
32133 * array(
32134 * 'url' => 'http:/.../front.jpeg',
32135 * 'rel' => 'back',
32136 * 'type' => 'image/jpeg',
32137 * )
32138 * )
32139 * </code>
32140 *
32141 * @return array
32142 */
32143 public function getLinks()
32144 {
32145 $links = $this->parseParams();
32146
32147 foreach ($links as &$link) {
32148 $key = key($link);
32149 unset($link[$key]);
32150 $link['url'] = trim($key, '<> ');
32151 }
32152
32153 return $links;
32154 }
32155 }
32156 <?php
32157
32158 namespace Guzzle\Http\Message\Header;
32159
32160 use Guzzle\Http\Message\Header;
32161
32162 /**
32163 * Default header factory implementation
32164 */
32165 class HeaderFactory implements HeaderFactoryInterface
32166 {
32167 /** @var array */
32168 protected $mapping = array(
32169 'cache-control' => 'Guzzle\Http\Message\Header\CacheControl',
32170 'link' => 'Guzzle\Http\Message\Header\Link',
32171 );
32172
32173 public function createHeader($header, $value = null)
32174 {
32175 $lowercase = strtolower($header);
32176
32177 return isset($this->mapping[$lowercase])
32178 ? new $this->mapping[$lowercase]($header, $value)
32179 : new Header($header, $value);
32180 }
32181 }
32182 <?php
32183
32184 namespace Guzzle\Http\Message\Header;
32185
32186 use Guzzle\Http\Message\Header;
32187
32188 /**
32189 * Provides helpful functionality for Cache-Control headers
32190 */
32191 class CacheControl extends Header
32192 {
32193 /** @var array */
32194 protected $directives;
32195
32196 public function add($value)
32197 {
32198 parent::add($value);
32199 $this->directives = null;
32200 }
32201
32202 public function removeValue($searchValue)
32203 {
32204 parent::removeValue($searchValue);
32205 $this->directives = null;
32206 }
32207
32208 /**
32209 * Check if a specific cache control directive exists
32210 *
32211 * @param string $param Directive to retrieve
32212 *
32213 * @return bool
32214 */
32215 public function hasDirective($param)
32216 {
32217 $directives = $this->getDirectives();
32218
32219 return isset($directives[$param]);
32220 }
32221
32222 /**
32223 * Get a specific cache control directive
32224 *
32225 * @param string $param Directive to retrieve
32226 *
32227 * @return string|bool|null
32228 */
32229 public function getDirective($param)
32230 {
32231 $directives = $this->getDirectives();
32232
32233 return isset($directives[$param]) ? $directives[$param] : null;
32234 }
32235
32236 /**
32237 * Add a cache control directive
32238 *
32239 * @param string $param Directive to add
32240 * @param string $value Value to set
32241 *
32242 * @return self
32243 */
32244 public function addDirective($param, $value)
32245 {
32246 $directives = $this->getDirectives();
32247 $directives[$param] = $value;
32248 $this->updateFromDirectives($directives);
32249
32250 return $this;
32251 }
32252
32253 /**
32254 * Remove a cache control directive by name
32255 *
32256 * @param string $param Directive to remove
32257 *
32258 * @return self
32259 */
32260 public function removeDirective($param)
32261 {
32262 $directives = $this->getDirectives();
32263 unset($directives[$param]);
32264 $this->updateFromDirectives($directives);
32265
32266 return $this;
32267 }
32268
32269 /**
32270 * Get an associative array of cache control directives
32271 *
32272 * @return array
32273 */
32274 public function getDirectives()
32275 {
32276 if ($this->directives === null) {
32277 $this->directives = array();
32278 foreach ($this->parseParams() as $collection) {
32279 foreach ($collection as $key => $value) {
32280 $this->directives[$key] = $value === '' ? true : $value;
32281 }
32282 }
32283 }
32284
32285 return $this->directives;
32286 }
32287
32288 /**
32289 * Updates the header value based on the parsed directives
32290 *
32291 * @param array $directives Array of cache control directives
32292 */
32293 protected function updateFromDirectives(array $directives)
32294 {
32295 $this->directives = $directives;
32296 $this->values = array();
32297
32298 foreach ($directives as $key => $value) {
32299 $this->values[] = $value === true ? $key : "{$key}={$value}";
32300 }
32301 }
32302 }
32303 <?php
32304
32305 namespace Guzzle\Http\Message\Header;
32306
32307 use Guzzle\Common\ToArrayInterface;
32308
32309 interface HeaderInterface extends ToArrayInterface, \Countable, \IteratorAggregate
32310 {
32311 /**
32312 * Convert the header to a string
32313 *
32314 * @return string
32315 */
32316 public function __toString();
32317
32318 /**
32319 * Add a value to the list of header values
32320 *
32321 * @param string $value Value to add to the header
32322 *
32323 * @return self
32324 */
32325 public function add($value);
32326
32327 /**
32328 * Get the name of the header
32329 *
32330 * @return string
32331 */
32332 public function getName();
32333
32334 /**
32335 * Change the name of the header
32336 *
32337 * @param string $name Name to change to
32338 *
32339 * @return self
32340 */
32341 public function setName($name);
32342
32343 /**
32344 * Change the glue used to implode the values
32345 *
32346 * @param string $glue Glue used to implode multiple values
32347 *
32348 * @return self
32349 */
32350 public function setGlue($glue);
32351
32352 /**
32353 * Get the glue used to implode multiple values into a string
32354 *
32355 * @return string
32356 */
32357 public function getGlue();
32358
32359 /**
32360 * Check if the collection of headers has a particular value
32361 *
32362 * @param string $searchValue Value to search for
32363 *
32364 * @return bool
32365 */
32366 public function hasValue($searchValue);
32367
32368 /**
32369 * Remove a specific value from the header
32370 *
32371 * @param string $searchValue Value to remove
32372 *
32373 * @return self
32374 */
32375 public function removeValue($searchValue);
32376
32377 /**
32378 * Parse a header containing ";" separated data into an array of associative arrays representing the header
32379 * key value pair data of the header. When a parameter does not contain a value, but just contains a key, this
32380 * function will inject a key with a '' string value.
32381 *
32382 * @return array
32383 */
32384 public function parseParams();
32385 }
32386 <?php
32387
32388 namespace Guzzle\Http\Message\Header;
32389
32390 use Guzzle\Common\Collection;
32391 use Guzzle\Common\ToArrayInterface;
32392
32393 /**
32394 * Provides a case-insensitive collection of headers
32395 */
32396 class HeaderCollection implements \IteratorAggregate, \Countable, \ArrayAccess, ToArrayInterface
32397 {
32398 /** @var array */
32399 protected $headers;
32400
32401 public function __construct($headers = array())
32402 {
32403 $this->headers = $headers;
32404 }
32405
32406 public function __clone()
32407 {
32408 foreach ($this->headers as &$header) {
32409 $header = clone $header;
32410 }
32411 }
32412
32413 /**
32414 * Clears the header collection
32415 */
32416 public function clear()
32417 {
32418 $this->headers = array();
32419 }
32420
32421 /**
32422 * Set a header on the collection
32423 *
32424 * @param HeaderInterface $header Header to add
32425 *
32426 * @return self
32427 */
32428 public function add(HeaderInterface $header)
32429 {
32430 $this->headers[strtolower($header->getName())] = $header;
32431
32432 return $this;
32433 }
32434
32435 /**
32436 * Get an array of header objects
32437 *
32438 * @return array
32439 */
32440 public function getAll()
32441 {
32442 return $this->headers;
32443 }
32444
32445 /**
32446 * Alias of offsetGet
32447 */
32448 public function get($key)
32449 {
32450 return $this->offsetGet($key);
32451 }
32452
32453 public function count()
32454 {
32455 return count($this->headers);
32456 }
32457
32458 public function offsetExists($offset)
32459 {
32460 return isset($this->headers[strtolower($offset)]);
32461 }
32462
32463 public function offsetGet($offset)
32464 {
32465 $l = strtolower($offset);
32466
32467 return isset($this->headers[$l]) ? $this->headers[$l] : null;
32468 }
32469
32470 public function offsetSet($offset, $value)
32471 {
32472 $this->add($value);
32473 }
32474
32475 public function offsetUnset($offset)
32476 {
32477 unset($this->headers[strtolower($offset)]);
32478 }
32479
32480 public function getIterator()
32481 {
32482 return new \ArrayIterator($this->headers);
32483 }
32484
32485 public function toArray()
32486 {
32487 $result = array();
32488 foreach ($this->headers as $header) {
32489 $result[$header->getName()] = $header->toArray();
32490 }
32491
32492 return $result;
32493 }
32494 }
32495 <?php
32496
32497 namespace Guzzle\Http\Message\Header;
32498
32499 /**
32500 * Interface for creating headers
32501 */
32502 interface HeaderFactoryInterface
32503 {
32504 /**
32505 * Create a header from a header name and a single value
32506 *
32507 * @param string $header Name of the header to create
32508 * @param string $value Value to set on the header
32509 *
32510 * @return HeaderInterface
32511 */
32512 public function createHeader($header, $value = null);
32513 }
32514 <?php
32515
32516 namespace Guzzle\Http\Message;
32517
32518 use Guzzle\Http\Exception\RequestException;
32519 use Guzzle\Http\EntityBodyInterface;
32520 use Guzzle\Http\QueryString;
32521
32522 /**
32523 * HTTP request that sends an entity-body in the request message (POST, PUT)
32524 */
32525 interface EntityEnclosingRequestInterface extends RequestInterface
32526 {
32527 const URL_ENCODED = 'application/x-www-form-urlencoded; charset=utf-8';
32528 const MULTIPART = 'multipart/form-data';
32529
32530 /**
32531 * Set the body of the request
32532 *
32533 * @param string|resource|EntityBodyInterface $body Body to use in the entity body of the request
32534 * @param string $contentType Content-Type to set. Leave null to use an existing
32535 * Content-Type or to guess the Content-Type
32536 * @return self
32537 * @throws RequestException if the protocol is < 1.1 and Content-Length can not be determined
32538 */
32539 public function setBody($body, $contentType = null);
32540
32541 /**
32542 * Get the body of the request if set
32543 *
32544 * @return EntityBodyInterface|null
32545 */
32546 public function getBody();
32547
32548 /**
32549 * Get a POST field from the request
32550 *
32551 * @param string $field Field to retrieve
32552 *
32553 * @return mixed|null
32554 */
32555 public function getPostField($field);
32556
32557 /**
32558 * Get the post fields that will be used in the request
32559 *
32560 * @return QueryString
32561 */
32562 public function getPostFields();
32563
32564 /**
32565 * Set a POST field value
32566 *
32567 * @param string $key Key to set
32568 * @param string $value Value to set
32569 *
32570 * @return self
32571 */
32572 public function setPostField($key, $value);
32573
32574 /**
32575 * Add POST fields to use in the request
32576 *
32577 * @param QueryString|array $fields POST fields
32578 *
32579 * @return self
32580 */
32581 public function addPostFields($fields);
32582
32583 /**
32584 * Remove a POST field or file by name
32585 *
32586 * @param string $field Name of the POST field or file to remove
32587 *
32588 * @return self
32589 */
32590 public function removePostField($field);
32591
32592 /**
32593 * Returns an associative array of POST field names to PostFileInterface objects
32594 *
32595 * @return array
32596 */
32597 public function getPostFiles();
32598
32599 /**
32600 * Get a POST file from the request
32601 *
32602 * @param string $fieldName POST fields to retrieve
32603 *
32604 * @return array|null Returns an array wrapping an array of PostFileInterface objects
32605 */
32606 public function getPostFile($fieldName);
32607
32608 /**
32609 * Remove a POST file from the request
32610 *
32611 * @param string $fieldName POST file field name to remove
32612 *
32613 * @return self
32614 */
32615 public function removePostFile($fieldName);
32616
32617 /**
32618 * Add a POST file to the upload
32619 *
32620 * @param string $field POST field to use (e.g. file). Used to reference content from the server.
32621 * @param string $filename Full path to the file. Do not include the @ symbol.
32622 * @param string $contentType Optional Content-Type to add to the Content-Disposition.
32623 * Default behavior is to guess. Set to false to not specify.
32624 * @return self
32625 */
32626 public function addPostFile($field, $filename = null, $contentType = null);
32627
32628 /**
32629 * Add POST files to use in the upload
32630 *
32631 * @param array $files An array of POST fields => filenames where filename can be a string or PostFileInterface
32632 *
32633 * @return self
32634 */
32635 public function addPostFiles(array $files);
32636
32637 /**
32638 * Configure how redirects are handled for the request
32639 *
32640 * @param bool $strict Set to true to follow strict RFC compliance when redirecting POST requests. Most
32641 * browsers with follow a 301-302 redirect for a POST request with a GET request. This is
32642 * the default behavior of Guzzle. Enable strict redirects to redirect these responses
32643 * with a POST rather than a GET request.
32644 * @param int $maxRedirects Specify the maximum number of allowed redirects. Set to 0 to disable redirects.
32645 *
32646 * @return self
32647 */
32648 public function configureRedirects($strict = false, $maxRedirects = 5);
32649 }
32650 <?php
32651
32652 namespace Guzzle\Http\Message;
32653
32654 use Guzzle\Common\Collection;
32655 use Guzzle\Http\EntityBodyInterface;
32656 use Guzzle\Http\Url;
32657
32658 /**
32659 * Request factory used to create HTTP requests
32660 */
32661 interface RequestFactoryInterface
32662 {
32663 const OPTIONS_NONE = 0;
32664 const OPTIONS_AS_DEFAULTS = 1;
32665
32666 /**
32667 * Create a new request based on an HTTP message
32668 *
32669 * @param string $message HTTP message as a string
32670 *
32671 * @return RequestInterface
32672 */
32673 public function fromMessage($message);
32674
32675 /**
32676 * Create a request from URL parts as returned from parse_url()
32677 *
32678 * @param string $method HTTP method (GET, POST, PUT, HEAD, DELETE, etc)
32679 *
32680 * @param array $urlParts URL parts containing the same keys as parse_url()
32681 * - scheme: e.g. http
32682 * - host: e.g. www.guzzle-project.com
32683 * - port: e.g. 80
32684 * - user: e.g. michael
32685 * - pass: e.g. rocks
32686 * - path: e.g. / OR /index.html
32687 * - query: after the question mark ?
32688 * @param array|Collection $headers HTTP headers
32689 * @param string|resource|array|EntityBodyInterface $body Body to send in the request
32690 * @param string $protocol Protocol (HTTP, SPYDY, etc)
32691 * @param string $protocolVersion 1.0, 1.1, etc
32692 *
32693 * @return RequestInterface
32694 */
32695 public function fromParts(
32696 $method,
32697 array $urlParts,
32698 $headers = null,
32699 $body = null,
32700 $protocol = 'HTTP',
32701 $protocolVersion = '1.1'
32702 );
32703
32704 /**
32705 * Create a new request based on the HTTP method
32706 *
32707 * @param string $method HTTP method (GET, POST, PUT, PATCH, HEAD, DELETE, ...)
32708 * @param string|Url $url HTTP URL to connect to
32709 * @param array|Collection $headers HTTP headers
32710 * @param string|resource|array|EntityBodyInterface $body Body to send in the request
32711 * @param array $options Array of options to apply to the request
32712 *
32713 * @return RequestInterface
32714 */
32715 public function create($method, $url, $headers = null, $body = null, array $options = array());
32716
32717 /**
32718 * Apply an associative array of options to the request
32719 *
32720 * @param RequestInterface $request Request to update
32721 * @param array $options Options to use with the request. Available options are:
32722 * "headers": Associative array of headers
32723 * "query": Associative array of query string values to add to the request
32724 * "body": Body of a request, including an EntityBody, string, or array when sending POST requests.
32725 * "auth": Array of HTTP authentication parameters to use with the request. The array must contain the
32726 * username in index [0], the password in index [2], and can optionally contain the authentication type
32727 * in index [3]. The authentication types are: "Basic", "Digest", "NTLM", "Any" (defaults to "Basic").
32728 * "cookies": Associative array of cookies
32729 * "allow_redirects": Set to false to disable redirects
32730 * "save_to": String, fopen resource, or EntityBody object used to store the body of the response
32731 * "events": Associative array mapping event names to a closure or array of (priority, closure)
32732 * "plugins": Array of plugins to add to the request
32733 * "exceptions": Set to false to disable throwing exceptions on an HTTP level error (e.g. 404, 500, etc)
32734 * "params": Set custom request data parameters on a request. (Note: these are not query string parameters)
32735 * "timeout": Float describing the timeout of the request in seconds
32736 * "connect_timeout": Float describing the number of seconds to wait while trying to connect. Use 0 to wait
32737 * indefinitely.
32738 * "verify": Set to true to enable SSL cert validation (the default), false to disable, or supply the path
32739 * to a CA bundle to enable verification using a custom certificate.
32740 * "cert": Set to a string to specify the path to a file containing a PEM formatted certificate. If a
32741 * password is required, then set an array containing the path to the PEM file followed by the the
32742 * password required for the certificate.
32743 * "ssl_key": Specify the path to a file containing a private SSL key in PEM format. If a password is
32744 * required, then set an array containing the path to the SSL key followed by the password required for
32745 * the certificate.
32746 * "proxy": Specify an HTTP proxy (e.g. "http://username:password@192.168.16.1:10")
32747 * "debug": Set to true to display all data sent over the wire
32748 * @param int $flags Bitwise flags to apply when applying the options to the request. Defaults to no special
32749 * options. `1` (OPTIONS_AS_DEFAULTS): When specified, options will only update a request when
32750 * the value does not already exist on the request. This is only supported by "query" and
32751 * "headers". Other bitwise options may be added in the future.
32752 */
32753 public function applyOptions(RequestInterface $request, array $options = array(), $flags = self::OPTIONS_NONE);
32754 }
32755 <?php
32756
32757 namespace Guzzle\Http\Message;
32758
32759 /**
32760 * Request and response message interface
32761 */
32762 interface MessageInterface
32763 {
32764 /**
32765 * Get application and plugin specific parameters set on the message.
32766 *
32767 * @return \Guzzle\Common\Collection
32768 */
32769 public function getParams();
32770
32771 /**
32772 * Add a header to an existing collection of headers.
32773 *
32774 * @param string $header Header name to add
32775 * @param string $value Value of the header
32776 *
32777 * @return self
32778 */
32779 public function addHeader($header, $value);
32780
32781 /**
32782 * Add and merge in an array of HTTP headers.
32783 *
32784 * @param array $headers Associative array of header data.
32785 *
32786 * @return self
32787 */
32788 public function addHeaders(array $headers);
32789
32790 /**
32791 * Retrieve an HTTP header by name. Performs a case-insensitive search of all headers.
32792 *
32793 * @param string $header Header to retrieve.
32794 *
32795 * @return Header|null
32796 */
32797 public function getHeader($header);
32798
32799 /**
32800 * Get all headers as a collection
32801 *
32802 * @return \Guzzle\Http\Message\Header\HeaderCollection
32803 */
32804 public function getHeaders();
32805
32806 /**
32807 * Check if the specified header is present.
32808 *
32809 * @param string $header The header to check.
32810 *
32811 * @return bool
32812 */
32813 public function hasHeader($header);
32814
32815 /**
32816 * Remove a specific HTTP header.
32817 *
32818 * @param string $header HTTP header to remove.
32819 *
32820 * @return self
32821 */
32822 public function removeHeader($header);
32823
32824 /**
32825 * Set an HTTP header and overwrite any existing value for the header
32826 *
32827 * @param string $header Name of the header to set.
32828 * @param mixed $value Value to set.
32829 *
32830 * @return self
32831 */
32832 public function setHeader($header, $value);
32833
32834 /**
32835 * Overwrite all HTTP headers with the supplied array of headers
32836 *
32837 * @param array $headers Associative array of header data.
32838 *
32839 * @return self
32840 */
32841 public function setHeaders(array $headers);
32842
32843 /**
32844 * Get an array of message header lines (e.g. ["Host: example.com", ...])
32845 *
32846 * @return array
32847 */
32848 public function getHeaderLines();
32849
32850 /**
32851 * Get the raw message headers as a string
32852 *
32853 * @return string
32854 */
32855 public function getRawHeaders();
32856 }
32857 <?php
32858
32859 namespace Guzzle\Http\Message;
32860
32861 use Guzzle\Common\Exception\InvalidArgumentException;
32862
32863 /**
32864 * POST file upload
32865 */
32866 interface PostFileInterface
32867 {
32868 /**
32869 * Set the name of the field
32870 *
32871 * @param string $name Field name
32872 *
32873 * @return self
32874 */
32875 public function setFieldName($name);
32876
32877 /**
32878 * Get the name of the field
32879 *
32880 * @return string
32881 */
32882 public function getFieldName();
32883
32884 /**
32885 * Set the path to the file
32886 *
32887 * @param string $path Full path to the file
32888 *
32889 * @return self
32890 * @throws InvalidArgumentException if the file cannot be read
32891 */
32892 public function setFilename($path);
32893
32894 /**
32895 * Get the full path to the file
32896 *
32897 * @return string
32898 */
32899 public function getFilename();
32900
32901 /**
32902 * Set the Content-Type of the file
32903 *
32904 * @param string $type Content type
32905 *
32906 * @return self
32907 */
32908 public function setContentType($type);
32909
32910 /**
32911 * Get the Content-Type of the file
32912 *
32913 * @return string
32914 */
32915 public function getContentType();
32916
32917 /**
32918 * Get a cURL ready string or CurlFile object for the upload
32919 *
32920 * @return string
32921 */
32922 public function getCurlValue();
32923 }
32924 <?php
32925
32926 namespace Guzzle\Http\Message;
32927
32928 use Guzzle\Common\Version;
32929 use Guzzle\Common\ToArrayInterface;
32930 use Guzzle\Common\Exception\RuntimeException;
32931 use Guzzle\Http\EntityBodyInterface;
32932 use Guzzle\Http\EntityBody;
32933 use Guzzle\Http\Exception\BadResponseException;
32934 use Guzzle\Http\RedirectPlugin;
32935 use Guzzle\Parser\ParserRegistry;
32936
32937 /**
32938 * Guzzle HTTP response object
32939 */
32940 class Response extends AbstractMessage implements \Serializable
32941 {
32942 /**
32943 * @var array Array of reason phrases and their corresponding status codes
32944 */
32945 private static $statusTexts = array(
32946 100 => 'Continue',
32947 101 => 'Switching Protocols',
32948 102 => 'Processing',
32949 200 => 'OK',
32950 201 => 'Created',
32951 202 => 'Accepted',
32952 203 => 'Non-Authoritative Information',
32953 204 => 'No Content',
32954 205 => 'Reset Content',
32955 206 => 'Partial Content',
32956 207 => 'Multi-Status',
32957 208 => 'Already Reported',
32958 226 => 'IM Used',
32959 300 => 'Multiple Choices',
32960 301 => 'Moved Permanently',
32961 302 => 'Found',
32962 303 => 'See Other',
32963 304 => 'Not Modified',
32964 305 => 'Use Proxy',
32965 307 => 'Temporary Redirect',
32966 308 => 'Permanent Redirect',
32967 400 => 'Bad Request',
32968 401 => 'Unauthorized',
32969 402 => 'Payment Required',
32970 403 => 'Forbidden',
32971 404 => 'Not Found',
32972 405 => 'Method Not Allowed',
32973 406 => 'Not Acceptable',
32974 407 => 'Proxy Authentication Required',
32975 408 => 'Request Timeout',
32976 409 => 'Conflict',
32977 410 => 'Gone',
32978 411 => 'Length Required',
32979 412 => 'Precondition Failed',
32980 413 => 'Request Entity Too Large',
32981 414 => 'Request-URI Too Long',
32982 415 => 'Unsupported Media Type',
32983 416 => 'Requested Range Not Satisfiable',
32984 417 => 'Expectation Failed',
32985 422 => 'Unprocessable Entity',
32986 423 => 'Locked',
32987 424 => 'Failed Dependency',
32988 425 => 'Reserved for WebDAV advanced collections expired proposal',
32989 426 => 'Upgrade required',
32990 428 => 'Precondition Required',
32991 429 => 'Too Many Requests',
32992 431 => 'Request Header Fields Too Large',
32993 500 => 'Internal Server Error',
32994 501 => 'Not Implemented',
32995 502 => 'Bad Gateway',
32996 503 => 'Service Unavailable',
32997 504 => 'Gateway Timeout',
32998 505 => 'HTTP Version Not Supported',
32999 506 => 'Variant Also Negotiates (Experimental)',
33000 507 => 'Insufficient Storage',
33001 508 => 'Loop Detected',
33002 510 => 'Not Extended',
33003 511 => 'Network Authentication Required',
33004 );
33005
33006 /** @var EntityBodyInterface The response body */
33007 protected $body;
33008
33009 /** @var string The reason phrase of the response (human readable code) */
33010 protected $reasonPhrase;
33011
33012 /** @var string The status code of the response */
33013 protected $statusCode;
33014
33015 /** @var array Information about the request */
33016 protected $info = array();
33017
33018 /** @var string The effective URL that returned this response */
33019 protected $effectiveUrl;
33020
33021 /** @var array Cacheable response codes (see RFC 2616:13.4) */
33022 protected static $cacheResponseCodes = array(200, 203, 206, 300, 301, 410);
33023
33024 /**
33025 * Create a new Response based on a raw response message
33026 *
33027 * @param string $message Response message
33028 *
33029 * @return self|bool Returns false on error
33030 */
33031 public static function fromMessage($message)
33032 {
33033 $data = ParserRegistry::getInstance()->getParser('message')->parseResponse($message);
33034 if (!$data) {
33035 return false;
33036 }
33037
33038 $response = new static($data['code'], $data['headers'], $data['body']);
33039 $response->setProtocol($data['protocol'], $data['version'])
33040 ->setStatus($data['code'], $data['reason_phrase']);
33041
33042 // Set the appropriate Content-Length if the one set is inaccurate (e.g. setting to X)
33043 $contentLength = (string) $response->getHeader('Content-Length');
33044 $actualLength = strlen($data['body']);
33045 if (strlen($data['body']) > 0 && $contentLength != $actualLength) {
33046 $response->setHeader('Content-Length', $actualLength);
33047 }
33048
33049 return $response;
33050 }
33051
33052 /**
33053 * Construct the response
33054 *
33055 * @param string $statusCode The response status code (e.g. 200, 404, etc)
33056 * @param ToArrayInterface|array $headers The response headers
33057 * @param string|resource|EntityBodyInterface $body The body of the response
33058 *
33059 * @throws BadResponseException if an invalid response code is given
33060 */
33061 public function __construct($statusCode, $headers = null, $body = null)
33062 {
33063 parent::__construct();
33064 $this->setStatus($statusCode);
33065 $this->body = EntityBody::factory($body !== null ? $body : '');
33066
33067 if ($headers) {
33068 if (is_array($headers)) {
33069 $this->setHeaders($headers);
33070 } elseif ($headers instanceof ToArrayInterface) {
33071 $this->setHeaders($headers->toArray());
33072 } else {
33073 throw new BadResponseException('Invalid headers argument received');
33074 }
33075 }
33076 }
33077
33078 /**
33079 * @return string
33080 */
33081 public function __toString()
33082 {
33083 return $this->getMessage();
33084 }
33085
33086 public function serialize()
33087 {
33088 return json_encode(array(
33089 'status' => $this->statusCode,
33090 'body' => (string) $this->body,
33091 'headers' => $this->headers->toArray()
33092 ));
33093 }
33094
33095 public function unserialize($serialize)
33096 {
33097 $data = json_decode($serialize, true);
33098 $this->__construct($data['status'], $data['headers'], $data['body']);
33099 }
33100
33101 /**
33102 * Get the response entity body
33103 *
33104 * @param bool $asString Set to TRUE to return a string of the body rather than a full body object
33105 *
33106 * @return EntityBodyInterface|string
33107 */
33108 public function getBody($asString = false)
33109 {
33110 return $asString ? (string) $this->body : $this->body;
33111 }
33112
33113 /**
33114 * Set the response entity body
33115 *
33116 * @param EntityBodyInterface|string $body Body to set
33117 *
33118 * @return self
33119 */
33120 public function setBody($body)
33121 {
33122 $this->body = EntityBody::factory($body);
33123
33124 return $this;
33125 }
33126
33127 /**
33128 * Set the protocol and protocol version of the response
33129 *
33130 * @param string $protocol Response protocol
33131 * @param string $version Protocol version
33132 *
33133 * @return self
33134 */
33135 public function setProtocol($protocol, $version)
33136 {
33137 $this->protocol = $protocol;
33138 $this->protocolVersion = $version;
33139
33140 return $this;
33141 }
33142
33143 /**
33144 * Get the protocol used for the response (e.g. HTTP)
33145 *
33146 * @return string
33147 */
33148 public function getProtocol()
33149 {
33150 return $this->protocol;
33151 }
33152
33153 /**
33154 * Get the HTTP protocol version
33155 *
33156 * @return string
33157 */
33158 public function getProtocolVersion()
33159 {
33160 return $this->protocolVersion;
33161 }
33162
33163 /**
33164 * Get a cURL transfer information
33165 *
33166 * @param string $key A single statistic to check
33167 *
33168 * @return array|string|null Returns all stats if no key is set, a single stat if a key is set, or null if a key
33169 * is set and not found
33170 * @link http://www.php.net/manual/en/function.curl-getinfo.php
33171 */
33172 public function getInfo($key = null)
33173 {
33174 if ($key === null) {
33175 return $this->info;
33176 } elseif (array_key_exists($key, $this->info)) {
33177 return $this->info[$key];
33178 } else {
33179 return null;
33180 }
33181 }
33182
33183 /**
33184 * Set the transfer information
33185 *
33186 * @param array $info Array of cURL transfer stats
33187 *
33188 * @return self
33189 */
33190 public function setInfo(array $info)
33191 {
33192 $this->info = $info;
33193
33194 return $this;
33195 }
33196
33197 /**
33198 * Set the response status
33199 *
33200 * @param int $statusCode Response status code to set
33201 * @param string $reasonPhrase Response reason phrase
33202 *
33203 * @return self
33204 * @throws BadResponseException when an invalid response code is received
33205 */
33206 public function setStatus($statusCode, $reasonPhrase = '')
33207 {
33208 $this->statusCode = (int) $statusCode;
33209
33210 if (!$reasonPhrase && isset(self::$statusTexts[$this->statusCode])) {
33211 $this->reasonPhrase = self::$statusTexts[$this->statusCode];
33212 } else {
33213 $this->reasonPhrase = $reasonPhrase;
33214 }
33215
33216 return $this;
33217 }
33218
33219 /**
33220 * Get the response status code
33221 *
33222 * @return integer
33223 */
33224 public function getStatusCode()
33225 {
33226 return $this->statusCode;
33227 }
33228
33229 /**
33230 * Get the entire response as a string
33231 *
33232 * @return string
33233 */
33234 public function getMessage()
33235 {
33236 $message = $this->getRawHeaders();
33237
33238 // Only include the body in the message if the size is < 2MB
33239 $size = $this->body->getSize();
33240 if ($size < 2097152) {
33241 $message .= (string) $this->body;
33242 }
33243
33244 return $message;
33245 }
33246
33247 /**
33248 * Get the the raw message headers as a string
33249 *
33250 * @return string
33251 */
33252 public function getRawHeaders()
33253 {
33254 $headers = 'HTTP/1.1 ' . $this->statusCode . ' ' . $this->reasonPhrase . "\r\n";
33255 $lines = $this->getHeaderLines();
33256 if (!empty($lines)) {
33257 $headers .= implode("\r\n", $lines) . "\r\n";
33258 }
33259
33260 return $headers . "\r\n";
33261 }
33262
33263 /**
33264 * Get the response reason phrase- a human readable version of the numeric
33265 * status code
33266 *
33267 * @return string
33268 */
33269 public function getReasonPhrase()
33270 {
33271 return $this->reasonPhrase;
33272 }
33273
33274 /**
33275 * Get the Accept-Ranges HTTP header
33276 *
33277 * @return string Returns what partial content range types this server supports.
33278 */
33279 public function getAcceptRanges()
33280 {
33281 return (string) $this->getHeader('Accept-Ranges');
33282 }
33283
33284 /**
33285 * Calculate the age of the response
33286 *
33287 * @return integer
33288 */
33289 public function calculateAge()
33290 {
33291 $age = $this->getHeader('Age');
33292
33293 if ($age === null && $this->getDate()) {
33294 $age = time() - strtotime($this->getDate());
33295 }
33296
33297 return $age === null ? null : (int) (string) $age;
33298 }
33299
33300 /**
33301 * Get the Age HTTP header
33302 *
33303 * @return integer|null Returns the age the object has been in a proxy cache in seconds.
33304 */
33305 public function getAge()
33306 {
33307 return (string) $this->getHeader('Age');
33308 }
33309
33310 /**
33311 * Get the Allow HTTP header
33312 *
33313 * @return string|null Returns valid actions for a specified resource. To be used for a 405 Method not allowed.
33314 */
33315 public function getAllow()
33316 {
33317 return (string) $this->getHeader('Allow');
33318 }
33319
33320 /**
33321 * Check if an HTTP method is allowed by checking the Allow response header
33322 *
33323 * @param string $method Method to check
33324 *
33325 * @return bool
33326 */
33327 public function isMethodAllowed($method)
33328 {
33329 $allow = $this->getHeader('Allow');
33330 if ($allow) {
33331 foreach (explode(',', $allow) as $allowable) {
33332 if (!strcasecmp(trim($allowable), $method)) {
33333 return true;
33334 }
33335 }
33336 }
33337
33338 return false;
33339 }
33340
33341 /**
33342 * Get the Cache-Control HTTP header
33343 *
33344 * @return string
33345 */
33346 public function getCacheControl()
33347 {
33348 return (string) $this->getHeader('Cache-Control');
33349 }
33350
33351 /**
33352 * Get the Connection HTTP header
33353 *
33354 * @return string
33355 */
33356 public function getConnection()
33357 {
33358 return (string) $this->getHeader('Connection');
33359 }
33360
33361 /**
33362 * Get the Content-Encoding HTTP header
33363 *
33364 * @return string|null
33365 */
33366 public function getContentEncoding()
33367 {
33368 return (string) $this->getHeader('Content-Encoding');
33369 }
33370
33371 /**
33372 * Get the Content-Language HTTP header
33373 *
33374 * @return string|null Returns the language the content is in.
33375 */
33376 public function getContentLanguage()
33377 {
33378 return (string) $this->getHeader('Content-Language');
33379 }
33380
33381 /**
33382 * Get the Content-Length HTTP header
33383 *
33384 * @return integer Returns the length of the response body in bytes
33385 */
33386 public function getContentLength()
33387 {
33388 return (int) (string) $this->getHeader('Content-Length');
33389 }
33390
33391 /**
33392 * Get the Content-Location HTTP header
33393 *
33394 * @return string|null Returns an alternate location for the returned data (e.g /index.htm)
33395 */
33396 public function getContentLocation()
33397 {
33398 return (string) $this->getHeader('Content-Location');
33399 }
33400
33401 /**
33402 * Get the Content-Disposition HTTP header
33403 *
33404 * @return string|null Returns the Content-Disposition header
33405 */
33406 public function getContentDisposition()
33407 {
33408 return (string) $this->getHeader('Content-Disposition');
33409 }
33410
33411 /**
33412 * Get the Content-MD5 HTTP header
33413 *
33414 * @return string|null Returns a Base64-encoded binary MD5 sum of the content of the response.
33415 */
33416 public function getContentMd5()
33417 {
33418 return (string) $this->getHeader('Content-MD5');
33419 }
33420
33421 /**
33422 * Get the Content-Range HTTP header
33423 *
33424 * @return string Returns where in a full body message this partial message belongs (e.g. bytes 21010-47021/47022).
33425 */
33426 public function getContentRange()
33427 {
33428 return (string) $this->getHeader('Content-Range');
33429 }
33430
33431 /**
33432 * Get the Content-Type HTTP header
33433 *
33434 * @return string Returns the mime type of this content.
33435 */
33436 public function getContentType()
33437 {
33438 return (string) $this->getHeader('Content-Type');
33439 }
33440
33441 /**
33442 * Checks if the Content-Type is of a certain type. This is useful if the
33443 * Content-Type header contains charset information and you need to know if
33444 * the Content-Type matches a particular type.
33445 *
33446 * @param string $type Content type to check against
33447 *
33448 * @return bool
33449 */
33450 public function isContentType($type)
33451 {
33452 return stripos($this->getHeader('Content-Type'), $type) !== false;
33453 }
33454
33455 /**
33456 * Get the Date HTTP header
33457 *
33458 * @return string|null Returns the date and time that the message was sent.
33459 */
33460 public function getDate()
33461 {
33462 return (string) $this->getHeader('Date');
33463 }
33464
33465 /**
33466 * Get the ETag HTTP header
33467 *
33468 * @return string|null Returns an identifier for a specific version of a resource, often a Message digest.
33469 */
33470 public function getEtag()
33471 {
33472 return (string) $this->getHeader('ETag');
33473 }
33474
33475 /**
33476 * Get the Expires HTTP header
33477 *
33478 * @return string|null Returns the date/time after which the response is considered stale.
33479 */
33480 public function getExpires()
33481 {
33482 return (string) $this->getHeader('Expires');
33483 }
33484
33485 /**
33486 * Get the Last-Modified HTTP header
33487 *
33488 * @return string|null Returns the last modified date for the requested object, in RFC 2822 format
33489 * (e.g. Tue, 15 Nov 1994 12:45:26 GMT)
33490 */
33491 public function getLastModified()
33492 {
33493 return (string) $this->getHeader('Last-Modified');
33494 }
33495
33496 /**
33497 * Get the Location HTTP header
33498 *
33499 * @return string|null Used in redirection, or when a new resource has been created.
33500 */
33501 public function getLocation()
33502 {
33503 return (string) $this->getHeader('Location');
33504 }
33505
33506 /**
33507 * Get the Pragma HTTP header
33508 *
33509 * @return Header|null Returns the implementation-specific headers that may have various effects anywhere along
33510 * the request-response chain.
33511 */
33512 public function getPragma()
33513 {
33514 return (string) $this->getHeader('Pragma');
33515 }
33516
33517 /**
33518 * Get the Proxy-Authenticate HTTP header
33519 *
33520 * @return string|null Authentication to access the proxy (e.g. Basic)
33521 */
33522 public function getProxyAuthenticate()
33523 {
33524 return (string) $this->getHeader('Proxy-Authenticate');
33525 }
33526
33527 /**
33528 * Get the Retry-After HTTP header
33529 *
33530 * @return int|null If an entity is temporarily unavailable, this instructs the client to try again after a
33531 * specified period of time.
33532 */
33533 public function getRetryAfter()
33534 {
33535 return (string) $this->getHeader('Retry-After');
33536 }
33537
33538 /**
33539 * Get the Server HTTP header
33540 *
33541 * @return string|null A name for the server
33542 */
33543 public function getServer()
33544 {
33545 return (string) $this->getHeader('Server');
33546 }
33547
33548 /**
33549 * Get the Set-Cookie HTTP header
33550 *
33551 * @return string|null An HTTP cookie.
33552 */
33553 public function getSetCookie()
33554 {
33555 return (string) $this->getHeader('Set-Cookie');
33556 }
33557
33558 /**
33559 * Get the Trailer HTTP header
33560 *
33561 * @return string|null The Trailer general field value indicates that the given set of header fields is present in
33562 * the trailer of a message encoded with chunked transfer-coding.
33563 */
33564 public function getTrailer()
33565 {
33566 return (string) $this->getHeader('Trailer');
33567 }
33568
33569 /**
33570 * Get the Transfer-Encoding HTTP header
33571 *
33572 * @return string|null The form of encoding used to safely transfer the entity to the user
33573 */
33574 public function getTransferEncoding()
33575 {
33576 return (string) $this->getHeader('Transfer-Encoding');
33577 }
33578
33579 /**
33580 * Get the Vary HTTP header
33581 *
33582 * @return string|null Tells downstream proxies how to match future request headers to decide whether the cached
33583 * response can be used rather than requesting a fresh one from the origin server.
33584 */
33585 public function getVary()
33586 {
33587 return (string) $this->getHeader('Vary');
33588 }
33589
33590 /**
33591 * Get the Via HTTP header
33592 *
33593 * @return string|null Informs the client of proxies through which the response was sent.
33594 */
33595 public function getVia()
33596 {
33597 return (string) $this->getHeader('Via');
33598 }
33599
33600 /**
33601 * Get the Warning HTTP header
33602 *
33603 * @return string|null A general warning about possible problems with the entity body
33604 */
33605 public function getWarning()
33606 {
33607 return (string) $this->getHeader('Warning');
33608 }
33609
33610 /**
33611 * Get the WWW-Authenticate HTTP header
33612 *
33613 * @return string|null Indicates the authentication scheme that should be used to access the requested entity
33614 */
33615 public function getWwwAuthenticate()
33616 {
33617 return (string) $this->getHeader('WWW-Authenticate');
33618 }
33619
33620 /**
33621 * Checks if HTTP Status code is a Client Error (4xx)
33622 *
33623 * @return bool
33624 */
33625 public function isClientError()
33626 {
33627 return $this->statusCode >= 400 && $this->statusCode < 500;
33628 }
33629
33630 /**
33631 * Checks if HTTP Status code is Server OR Client Error (4xx or 5xx)
33632 *
33633 * @return boolean
33634 */
33635 public function isError()
33636 {
33637 return $this->isClientError() || $this->isServerError();
33638 }
33639
33640 /**
33641 * Checks if HTTP Status code is Information (1xx)
33642 *
33643 * @return bool
33644 */
33645 public function isInformational()
33646 {
33647 return $this->statusCode < 200;
33648 }
33649
33650 /**
33651 * Checks if HTTP Status code is a Redirect (3xx)
33652 *
33653 * @return bool
33654 */
33655 public function isRedirect()
33656 {
33657 return $this->statusCode >= 300 && $this->statusCode < 400;
33658 }
33659
33660 /**
33661 * Checks if HTTP Status code is Server Error (5xx)
33662 *
33663 * @return bool
33664 */
33665 public function isServerError()
33666 {
33667 return $this->statusCode >= 500 && $this->statusCode < 600;
33668 }
33669
33670 /**
33671 * Checks if HTTP Status code is Successful (2xx | 304)
33672 *
33673 * @return bool
33674 */
33675 public function isSuccessful()
33676 {
33677 return ($this->statusCode >= 200 && $this->statusCode < 300) || $this->statusCode == 304;
33678 }
33679
33680 /**
33681 * Check if the response can be cached based on the response headers
33682 *
33683 * @return bool Returns TRUE if the response can be cached or false if not
33684 */
33685 public function canCache()
33686 {
33687 // Check if the response is cacheable based on the code
33688 if (!in_array((int) $this->getStatusCode(), self::$cacheResponseCodes)) {
33689 return false;
33690 }
33691
33692 // Make sure a valid body was returned and can be cached
33693 if ((!$this->getBody()->isReadable() || !$this->getBody()->isSeekable())
33694 && ($this->getContentLength() > 0 || $this->getTransferEncoding() == 'chunked')) {
33695 return false;
33696 }
33697
33698 // Never cache no-store resources (this is a private cache, so private
33699 // can be cached)
33700 if ($this->getHeader('Cache-Control') && $this->getHeader('Cache-Control')->hasDirective('no-store')) {
33701 return false;
33702 }
33703
33704 return $this->isFresh() || $this->getFreshness() === null || $this->canValidate();
33705 }
33706
33707 /**
33708 * Gets the number of seconds from the current time in which this response is still considered fresh
33709 *
33710 * @return int|null Returns the number of seconds
33711 */
33712 public function getMaxAge()
33713 {
33714 if ($header = $this->getHeader('Cache-Control')) {
33715 // s-max-age, then max-age, then Expires
33716 if ($age = $header->getDirective('s-maxage')) {
33717 return $age;
33718 }
33719 if ($age = $header->getDirective('max-age')) {
33720 return $age;
33721 }
33722 }
33723
33724 if ($this->getHeader('Expires')) {
33725 return strtotime($this->getExpires()) - time();
33726 }
33727
33728 return null;
33729 }
33730
33731 /**
33732 * Check if the response is considered fresh.
33733 *
33734 * A response is considered fresh when its age is less than or equal to the freshness lifetime (maximum age) of the
33735 * response.
33736 *
33737 * @return bool|null
33738 */
33739 public function isFresh()
33740 {
33741 $fresh = $this->getFreshness();
33742
33743 return $fresh === null ? null : $fresh >= 0;
33744 }
33745
33746 /**
33747 * Check if the response can be validated against the origin server using a conditional GET request.
33748 *
33749 * @return bool
33750 */
33751 public function canValidate()
33752 {
33753 return $this->getEtag() || $this->getLastModified();
33754 }
33755
33756 /**
33757 * Get the freshness of the response by returning the difference of the maximum lifetime of the response and the
33758 * age of the response (max-age - age).
33759 *
33760 * Freshness values less than 0 mean that the response is no longer fresh and is ABS(freshness) seconds expired.
33761 * Freshness values of greater than zero is the number of seconds until the response is no longer fresh. A NULL
33762 * result means that no freshness information is available.
33763 *
33764 * @return int
33765 */
33766 public function getFreshness()
33767 {
33768 $maxAge = $this->getMaxAge();
33769 $age = $this->calculateAge();
33770
33771 return $maxAge && $age ? ($maxAge - $age) : null;
33772 }
33773
33774 /**
33775 * Parse the JSON response body and return an array
33776 *
33777 * @return array|string|int|bool|float
33778 * @throws RuntimeException if the response body is not in JSON format
33779 */
33780 public function json()
33781 {
33782 $data = json_decode((string) $this->body, true);
33783 if (JSON_ERROR_NONE !== json_last_error()) {
33784 throw new RuntimeException('Unable to parse response body into JSON: ' . json_last_error());
33785 }
33786
33787 return $data === null ? array() : $data;
33788 }
33789
33790 /**
33791 * Parse the XML response body and return a SimpleXMLElement
33792 *
33793 * @return \SimpleXMLElement
33794 * @throws RuntimeException if the response body is not in XML format
33795 */
33796 public function xml()
33797 {
33798 try {
33799 // Allow XML to be retrieved even if there is no response body
33800 $xml = new \SimpleXMLElement((string) $this->body ?: '<root />');
33801 } catch (\Exception $e) {
33802 throw new RuntimeException('Unable to parse response body into XML: ' . $e->getMessage());
33803 }
33804
33805 return $xml;
33806 }
33807
33808 /**
33809 * Get the redirect count of this response
33810 *
33811 * @return int
33812 */
33813 public function getRedirectCount()
33814 {
33815 return (int) $this->params->get(RedirectPlugin::REDIRECT_COUNT);
33816 }
33817
33818 /**
33819 * Set the effective URL that resulted in this response (e.g. the last redirect URL)
33820 *
33821 * @param string $url The effective URL
33822 *
33823 * @return self
33824 */
33825 public function setEffectiveUrl($url)
33826 {
33827 $this->effectiveUrl = $url;
33828
33829 return $this;
33830 }
33831
33832 /**
33833 * Get the effective URL that resulted in this response (e.g. the last redirect URL)
33834 *
33835 * @return string
33836 */
33837 public function getEffectiveUrl()
33838 {
33839 return $this->effectiveUrl;
33840 }
33841
33842 /**
33843 * @deprecated
33844 * @codeCoverageIgnore
33845 */
33846 public function getPreviousResponse()
33847 {
33848 Version::warn(__METHOD__ . ' is deprecated. Use the HistoryPlugin.');
33849 return null;
33850 }
33851
33852 /**
33853 * @deprecated
33854 * @codeCoverageIgnore
33855 */
33856 public function setRequest($request)
33857 {
33858 Version::warn(__METHOD__ . ' is deprecated');
33859 return $this;
33860 }
33861
33862 /**
33863 * @deprecated
33864 * @codeCoverageIgnore
33865 */
33866 public function getRequest()
33867 {
33868 Version::warn(__METHOD__ . ' is deprecated');
33869 return null;
33870 }
33871 }
33872 <?php
33873
33874 namespace Guzzle\Http\Message;
33875
33876 use Guzzle\Common\Collection;
33877 use Guzzle\Common\Exception\InvalidArgumentException;
33878 use Guzzle\Http\RedirectPlugin;
33879 use Guzzle\Http\Url;
33880 use Guzzle\Parser\ParserRegistry;
33881 use Guzzle\Plugin\Log\LogPlugin;
33882
33883 /**
33884 * Default HTTP request factory used to create the default {@see Request} and {@see EntityEnclosingRequest} objects.
33885 */
33886 class RequestFactory implements RequestFactoryInterface
33887 {
33888 /** @var RequestFactory Singleton instance of the default request factory */
33889 protected static $instance;
33890
33891 /** @var array Hash of methods available to the class (provides fast isset() lookups) */
33892 protected $methods;
33893
33894 /** @var string Class to instantiate for requests with no body */
33895 protected $requestClass = 'Guzzle\\Http\\Message\\Request';
33896
33897 /** @var string Class to instantiate for requests with a body */
33898 protected $entityEnclosingRequestClass = 'Guzzle\\Http\\Message\\EntityEnclosingRequest';
33899
33900 /**
33901 * Get a cached instance of the default request factory
33902 *
33903 * @return RequestFactory
33904 */
33905 public static function getInstance()
33906 {
33907 // @codeCoverageIgnoreStart
33908 if (!static::$instance) {
33909 static::$instance = new static();
33910 }
33911 // @codeCoverageIgnoreEnd
33912
33913 return static::$instance;
33914 }
33915
33916 public function __construct()
33917 {
33918 $this->methods = array_flip(get_class_methods(__CLASS__));
33919 }
33920
33921 public function fromMessage($message)
33922 {
33923 $parsed = ParserRegistry::getInstance()->getParser('message')->parseRequest($message);
33924
33925 if (!$parsed) {
33926 return false;
33927 }
33928
33929 $request = $this->fromParts($parsed['method'], $parsed['request_url'],
33930 $parsed['headers'], $parsed['body'], $parsed['protocol'],
33931 $parsed['version']);
33932
33933 // EntityEnclosingRequest adds an "Expect: 100-Continue" header when using a raw request body for PUT or POST
33934 // requests. This factory method should accurately reflect the message, so here we are removing the Expect
33935 // header if one was not supplied in the message.
33936 if (!isset($parsed['headers']['Expect']) && !isset($parsed['headers']['expect'])) {
33937 $request->removeHeader('Expect');
33938 }
33939
33940 return $request;
33941 }
33942
33943 public function fromParts(
33944 $method,
33945 array $urlParts,
33946 $headers = null,
33947 $body = null,
33948 $protocol = 'HTTP',
33949 $protocolVersion = '1.1'
33950 ) {
33951 return $this->create($method, Url::buildUrl($urlParts), $headers, $body)
33952 ->setProtocolVersion($protocolVersion);
33953 }
33954
33955 public function create($method, $url, $headers = null, $body = null, array $options = array())
33956 {
33957 $method = strtoupper($method);
33958
33959 if ($method == 'GET' || $method == 'HEAD' || $method == 'TRACE' || $method == 'OPTIONS') {
33960 // Handle non-entity-enclosing request methods
33961 $request = new $this->requestClass($method, $url, $headers);
33962 if ($body) {
33963 // The body is where the response body will be stored
33964 $type = gettype($body);
33965 if ($type == 'string' || $type == 'resource' || $type == 'object') {
33966 $request->setResponseBody($body);
33967 }
33968 }
33969 } else {
33970 // Create an entity enclosing request by default
33971 $request = new $this->entityEnclosingRequestClass($method, $url, $headers);
33972 if ($body) {
33973 // Add POST fields and files to an entity enclosing request if an array is used
33974 if (is_array($body) || $body instanceof Collection) {
33975 // Normalize PHP style cURL uploads with a leading '@' symbol
33976 foreach ($body as $key => $value) {
33977 if (is_string($value) && substr($value, 0, 1) == '@') {
33978 $request->addPostFile($key, $value);
33979 unset($body[$key]);
33980 }
33981 }
33982 // Add the fields if they are still present and not all files
33983 $request->addPostFields($body);
33984 } else {
33985 // Add a raw entity body body to the request
33986 $request->setBody($body, (string) $request->getHeader('Content-Type'));
33987 if ((string) $request->getHeader('Transfer-Encoding') == 'chunked') {
33988 $request->removeHeader('Content-Length');
33989 }
33990 }
33991 }
33992 }
33993
33994 if ($options) {
33995 $this->applyOptions($request, $options);
33996 }
33997
33998 return $request;
33999 }
34000
34001 /**
34002 * Clone a request while changing the method. Emulates the behavior of
34003 * {@see Guzzle\Http\Message\Request::clone}, but can change the HTTP method.
34004 *
34005 * @param RequestInterface $request Request to clone
34006 * @param string $method Method to set
34007 *
34008 * @return RequestInterface
34009 */
34010 public function cloneRequestWithMethod(RequestInterface $request, $method)
34011 {
34012 // Create the request with the same client if possible
34013 if ($client = $request->getClient()) {
34014 $cloned = $request->getClient()->createRequest($method, $request->getUrl(), $request->getHeaders());
34015 } else {
34016 $cloned = $this->create($method, $request->getUrl(), $request->getHeaders());
34017 }
34018
34019 $cloned->getCurlOptions()->replace($request->getCurlOptions()->toArray());
34020 $cloned->setEventDispatcher(clone $request->getEventDispatcher());
34021 // Ensure that that the Content-Length header is not copied if changing to GET or HEAD
34022 if (!($cloned instanceof EntityEnclosingRequestInterface)) {
34023 $cloned->removeHeader('Content-Length');
34024 } elseif ($request instanceof EntityEnclosingRequestInterface) {
34025 $cloned->setBody($request->getBody());
34026 }
34027 $cloned->getParams()->replace($request->getParams()->toArray());
34028 $cloned->dispatch('request.clone', array('request' => $cloned));
34029
34030 return $cloned;
34031 }
34032
34033 public function applyOptions(RequestInterface $request, array $options = array(), $flags = self::OPTIONS_NONE)
34034 {
34035 // Iterate over each key value pair and attempt to apply a config using function visitors
34036 foreach ($options as $key => $value) {
34037 $method = "visit_{$key}";
34038 if (isset($this->methods[$method])) {
34039 $this->{$method}($request, $value, $flags);
34040 }
34041 }
34042 }
34043
34044 protected function visit_headers(RequestInterface $request, $value, $flags)
34045 {
34046 if (!is_array($value)) {
34047 throw new InvalidArgumentException('headers value must be an array');
34048 }
34049
34050 if ($flags & self::OPTIONS_AS_DEFAULTS) {
34051 // Merge headers in but do not overwrite existing values
34052 foreach ($value as $key => $header) {
34053 if (!$request->hasHeader($key)) {
34054 $request->setHeader($key, $header);
34055 }
34056 }
34057 } else {
34058 $request->addHeaders($value);
34059 }
34060 }
34061
34062 protected function visit_body(RequestInterface $request, $value, $flags)
34063 {
34064 if ($request instanceof EntityEnclosingRequestInterface) {
34065 $request->setBody($value);
34066 } else {
34067 throw new InvalidArgumentException('Attempting to set a body on a non-entity-enclosing request');
34068 }
34069 }
34070
34071 protected function visit_allow_redirects(RequestInterface $request, $value, $flags)
34072 {
34073 if ($value === false) {
34074 $request->getParams()->set(RedirectPlugin::DISABLE, true);
34075 }
34076 }
34077
34078 protected function visit_auth(RequestInterface $request, $value, $flags)
34079 {
34080 if (!is_array($value)) {
34081 throw new InvalidArgumentException('auth value must be an array');
34082 }
34083
34084 $request->setAuth($value[0], isset($value[1]) ? $value[1] : null, isset($value[2]) ? $value[2] : 'basic');
34085 }
34086
34087 protected function visit_query(RequestInterface $request, $value, $flags)
34088 {
34089 if (!is_array($value)) {
34090 throw new InvalidArgumentException('query value must be an array');
34091 }
34092
34093 if ($flags & self::OPTIONS_AS_DEFAULTS) {
34094 // Merge query string values in but do not overwrite existing values
34095 $query = $request->getQuery();
34096 $query->overwriteWith(array_diff_key($value, $query->toArray()));
34097 } else {
34098 $request->getQuery()->overwriteWith($value);
34099 }
34100 }
34101
34102 protected function visit_cookies(RequestInterface $request, $value, $flags)
34103 {
34104 if (!is_array($value)) {
34105 throw new InvalidArgumentException('cookies value must be an array');
34106 }
34107
34108 foreach ($value as $name => $v) {
34109 $request->addCookie($name, $v);
34110 }
34111 }
34112
34113 protected function visit_events(RequestInterface $request, $value, $flags)
34114 {
34115 if (!is_array($value)) {
34116 throw new InvalidArgumentException('events value must be an array');
34117 }
34118
34119 foreach ($value as $name => $method) {
34120 if (is_array($method)) {
34121 $request->getEventDispatcher()->addListener($name, $method[0], $method[1]);
34122 } else {
34123 $request->getEventDispatcher()->addListener($name, $method);
34124 }
34125 }
34126 }
34127
34128 protected function visit_plugins(RequestInterface $request, $value, $flags)
34129 {
34130 if (!is_array($value)) {
34131 throw new InvalidArgumentException('plugins value must be an array');
34132 }
34133
34134 foreach ($value as $plugin) {
34135 $request->addSubscriber($plugin);
34136 }
34137 }
34138
34139 protected function visit_exceptions(RequestInterface $request, $value, $flags)
34140 {
34141 if ($value === false || $value === 0) {
34142 $dispatcher = $request->getEventDispatcher();
34143 foreach ($dispatcher->getListeners('request.error') as $listener) {
34144 if (is_array($listener) && $listener[0] == 'Guzzle\Http\Message\Request' && $listener[1] = 'onRequestError') {
34145 $dispatcher->removeListener('request.error', $listener);
34146 break;
34147 }
34148 }
34149 }
34150 }
34151
34152 protected function visit_save_to(RequestInterface $request, $value, $flags)
34153 {
34154 $request->setResponseBody($value);
34155 }
34156
34157 protected function visit_params(RequestInterface $request, $value, $flags)
34158 {
34159 if (!is_array($value)) {
34160 throw new InvalidArgumentException('params value must be an array');
34161 }
34162
34163 $request->getParams()->overwriteWith($value);
34164 }
34165
34166 protected function visit_timeout(RequestInterface $request, $value, $flags)
34167 {
34168 $request->getCurlOptions()->set(CURLOPT_TIMEOUT_MS, $value * 1000);
34169 }
34170
34171 protected function visit_connect_timeout(RequestInterface $request, $value, $flags)
34172 {
34173 $request->getCurlOptions()->set(CURLOPT_CONNECTTIMEOUT_MS, $value * 1000);
34174 }
34175
34176 protected function visit_debug(RequestInterface $request, $value, $flags)
34177 {
34178 if (class_exists('Guzzle\Plugin\Log\LogPlugin')) {
34179 $request->addSubscriber(LogPlugin::getDebugPlugin());
34180 } else {
34181 // @codeCoverageIgnoreStart
34182 $request->getCurlOptions()->set(CURLOPT_VERBOSE, true);
34183 // @codeCoverageIgnoreEnd
34184 }
34185 }
34186
34187 protected function visit_verify(RequestInterface $request, $value, $flags)
34188 {
34189 $curl = $request->getCurlOptions();
34190 if ($value === true || is_string($value)) {
34191 $curl[CURLOPT_SSL_VERIFYHOST] = 2;
34192 $curl[CURLOPT_SSL_VERIFYPEER] = true;
34193 if ($value !== true) {
34194 $curl[CURLOPT_CAINFO] = $value;
34195 }
34196 } elseif ($value === false) {
34197 unset($curl[CURLOPT_CAINFO]);
34198 $curl[CURLOPT_SSL_VERIFYHOST] = 0;
34199 $curl[CURLOPT_SSL_VERIFYPEER] = false;
34200 }
34201 }
34202
34203 protected function visit_proxy(RequestInterface $request, $value, $flags)
34204 {
34205 $request->getCurlOptions()->set(CURLOPT_PROXY, $value, $flags);
34206 }
34207
34208 protected function visit_cert(RequestInterface $request, $value, $flags)
34209 {
34210 if (is_array($value)) {
34211 $request->getCurlOptions()->set(CURLOPT_SSLCERT, $value[0]);
34212 $request->getCurlOptions()->set(CURLOPT_SSLCERTPASSWD, $value[1]);
34213 } else {
34214 $request->getCurlOptions()->set(CURLOPT_SSLCERT, $value);
34215 }
34216 }
34217
34218 protected function visit_ssl_key(RequestInterface $request, $value, $flags)
34219 {
34220 if (is_array($value)) {
34221 $request->getCurlOptions()->set(CURLOPT_SSLKEY, $value[0]);
34222 $request->getCurlOptions()->set(CURLOPT_SSLKEYPASSWD, $value[1]);
34223 } else {
34224 $request->getCurlOptions()->set(CURLOPT_SSLKEY, $value);
34225 }
34226 }
34227 }
34228 ##
34229 ## ca-bundle.crt -- Bundle of CA Root Certificates
34230 ##
34231 ## Certificate data from Mozilla as of: Sat Dec 29 20:03:40 2012
34232 ##
34233 ## This is a bundle of X.509 certificates of public Certificate Authorities
34234 ## (CA). These were automatically extracted from Mozilla's root certificates
34235 ## file (certdata.txt). This file can be found in the mozilla source tree:
34236 ## http://mxr.mozilla.org/mozilla/source/security/nss/lib/ckfw/builtins/certdata.txt?raw=1
34237 ##
34238 ## It contains the certificates in PEM format and therefore
34239 ## can be directly used with curl / libcurl / php_curl, or with
34240 ## an Apache+mod_ssl webserver for SSL client authentication.
34241 ## Just configure this file as the SSLCACertificateFile.
34242 ##
34243
34244 # @(#) $RCSfile: certdata.txt,v $ $Revision: 1.87 $ $Date: 2012/12/29 16:32:45 $
34245
34246 GTE CyberTrust Global Root
34247 ==========================
34248 -----BEGIN CERTIFICATE-----
34249 MIICWjCCAcMCAgGlMA0GCSqGSIb3DQEBBAUAMHUxCzAJBgNVBAYTAlVTMRgwFgYDVQQKEw9HVEUg
34250 Q29ycG9yYXRpb24xJzAlBgNVBAsTHkdURSBDeWJlclRydXN0IFNvbHV0aW9ucywgSW5jLjEjMCEG
34251 A1UEAxMaR1RFIEN5YmVyVHJ1c3QgR2xvYmFsIFJvb3QwHhcNOTgwODEzMDAyOTAwWhcNMTgwODEz
34252 MjM1OTAwWjB1MQswCQYDVQQGEwJVUzEYMBYGA1UEChMPR1RFIENvcnBvcmF0aW9uMScwJQYDVQQL
34253 Ex5HVEUgQ3liZXJUcnVzdCBTb2x1dGlvbnMsIEluYy4xIzAhBgNVBAMTGkdURSBDeWJlclRydXN0
34254 IEdsb2JhbCBSb290MIGfMA0GCSqGSIb3DQEBAQUAA4GNADCBiQKBgQCVD6C28FCc6HrHiM3dFw4u
34255 sJTQGz0O9pTAipTHBsiQl8i4ZBp6fmw8U+E3KHNgf7KXUwefU/ltWJTSr41tiGeA5u2ylc9yMcql
34256 HHK6XALnZELn+aks1joNrI1CqiQBOeacPwGFVw1Yh0X404Wqk2kmhXBIgD8SFcd5tB8FLztimQID
34257 AQABMA0GCSqGSIb3DQEBBAUAA4GBAG3rGwnpXtlR22ciYaQqPEh346B8pt5zohQDhT37qw4wxYMW
34258 M4ETCJ57NE7fQMh017l93PR2VX2bY1QY6fDq81yx2YtCHrnAlU66+tXifPVoYb+O7AWXX1uw16OF
34259 NMQkpw0PlZPvy5TYnh+dXIVtx6quTx8itc2VrbqnzPmrC3p/
34260 -----END CERTIFICATE-----
34261
34262 Thawte Server CA
34263 ================
34264 -----BEGIN CERTIFICATE-----
34265 MIIDEzCCAnygAwIBAgIBATANBgkqhkiG9w0BAQQFADCBxDELMAkGA1UEBhMCWkExFTATBgNVBAgT
34266 DFdlc3Rlcm4gQ2FwZTESMBAGA1UEBxMJQ2FwZSBUb3duMR0wGwYDVQQKExRUaGF3dGUgQ29uc3Vs
34267 dGluZyBjYzEoMCYGA1UECxMfQ2VydGlmaWNhdGlvbiBTZXJ2aWNlcyBEaXZpc2lvbjEZMBcGA1UE
34268 AxMQVGhhd3RlIFNlcnZlciBDQTEmMCQGCSqGSIb3DQEJARYXc2VydmVyLWNlcnRzQHRoYXd0ZS5j
34269 b20wHhcNOTYwODAxMDAwMDAwWhcNMjAxMjMxMjM1OTU5WjCBxDELMAkGA1UEBhMCWkExFTATBgNV
34270 BAgTDFdlc3Rlcm4gQ2FwZTESMBAGA1UEBxMJQ2FwZSBUb3duMR0wGwYDVQQKExRUaGF3dGUgQ29u
34271 c3VsdGluZyBjYzEoMCYGA1UECxMfQ2VydGlmaWNhdGlvbiBTZXJ2aWNlcyBEaXZpc2lvbjEZMBcG
34272 A1UEAxMQVGhhd3RlIFNlcnZlciBDQTEmMCQGCSqGSIb3DQEJARYXc2VydmVyLWNlcnRzQHRoYXd0
34273 ZS5jb20wgZ8wDQYJKoZIhvcNAQEBBQADgY0AMIGJAoGBANOkUG7I/1Zr5s9dtuoMaHVHoqrC2oQl
34274 /Kj0R1HahbUgdJSGHg91yekIYfUGbTBuFRkC6VLAYttNmZ7iagxEOM3+vuNkCXDF/rFrKbYvScg7
34275 1CcEJRCXL+eQbcAoQpnXTEPew/UhbVSfXcNY4cDk2VuwuNy0e982OsK1ZiIS1ocNAgMBAAGjEzAR
34276 MA8GA1UdEwEB/wQFMAMBAf8wDQYJKoZIhvcNAQEEBQADgYEAB/pMaVz7lcxG7oWDTSEwjsrZqG9J
34277 GubaUeNgcGyEYRGhGshIPllDfU+VPaGLtwtimHp1it2ITk6eQNuozDJ0uW8NxuOzRAvZim+aKZuZ
34278 GCg70eNAKJpaPNW15yAbi8qkq43pUdniTCxZqdq5snUb9kLy78fyGPmJvKP/iiMucEc=
34279 -----END CERTIFICATE-----
34280
34281 Thawte Premium Server CA
34282 ========================
34283 -----BEGIN CERTIFICATE-----
34284 MIIDJzCCApCgAwIBAgIBATANBgkqhkiG9w0BAQQFADCBzjELMAkGA1UEBhMCWkExFTATBgNVBAgT
34285 DFdlc3Rlcm4gQ2FwZTESMBAGA1UEBxMJQ2FwZSBUb3duMR0wGwYDVQQKExRUaGF3dGUgQ29uc3Vs
34286 dGluZyBjYzEoMCYGA1UECxMfQ2VydGlmaWNhdGlvbiBTZXJ2aWNlcyBEaXZpc2lvbjEhMB8GA1UE
34287 AxMYVGhhd3RlIFByZW1pdW0gU2VydmVyIENBMSgwJgYJKoZIhvcNAQkBFhlwcmVtaXVtLXNlcnZl
34288 ckB0aGF3dGUuY29tMB4XDTk2MDgwMTAwMDAwMFoXDTIwMTIzMTIzNTk1OVowgc4xCzAJBgNVBAYT
34289 AlpBMRUwEwYDVQQIEwxXZXN0ZXJuIENhcGUxEjAQBgNVBAcTCUNhcGUgVG93bjEdMBsGA1UEChMU
34290 VGhhd3RlIENvbnN1bHRpbmcgY2MxKDAmBgNVBAsTH0NlcnRpZmljYXRpb24gU2VydmljZXMgRGl2
34291 aXNpb24xITAfBgNVBAMTGFRoYXd0ZSBQcmVtaXVtIFNlcnZlciBDQTEoMCYGCSqGSIb3DQEJARYZ
34292 cHJlbWl1bS1zZXJ2ZXJAdGhhd3RlLmNvbTCBnzANBgkqhkiG9w0BAQEFAAOBjQAwgYkCgYEA0jY2
34293 aovXwlue2oFBYo847kkEVdbQ7xwblRZH7xhINTpS9CtqBo87L+pW46+GjZ4X9560ZXUCTe/LCaIh
34294 Udib0GfQug2SBhRz1JPLlyoAnFxODLz6FVL88kRu2hFKbgifLy3j+ao6hnO2RlNYyIkFvYMRuHM/
34295 qgeN9EJN50CdHDcCAwEAAaMTMBEwDwYDVR0TAQH/BAUwAwEB/zANBgkqhkiG9w0BAQQFAAOBgQAm
34296 SCwWwlj66BZ0DKqqX1Q/8tfJeGBeXm43YyJ3Nn6yF8Q0ufUIhfzJATj/Tb7yFkJD57taRvvBxhEf
34297 8UqwKEbJw8RCfbz6q1lu1bdRiBHjpIUZa4JMpAwSremkrj/xw0llmozFyD4lt5SZu5IycQfwhl7t
34298 UCemDaYj+bvLpgcUQg==
34299 -----END CERTIFICATE-----
34300
34301 Equifax Secure CA
34302 =================
34303 -----BEGIN CERTIFICATE-----
34304 MIIDIDCCAomgAwIBAgIENd70zzANBgkqhkiG9w0BAQUFADBOMQswCQYDVQQGEwJVUzEQMA4GA1UE
34305 ChMHRXF1aWZheDEtMCsGA1UECxMkRXF1aWZheCBTZWN1cmUgQ2VydGlmaWNhdGUgQXV0aG9yaXR5
34306 MB4XDTk4MDgyMjE2NDE1MVoXDTE4MDgyMjE2NDE1MVowTjELMAkGA1UEBhMCVVMxEDAOBgNVBAoT
34307 B0VxdWlmYXgxLTArBgNVBAsTJEVxdWlmYXggU2VjdXJlIENlcnRpZmljYXRlIEF1dGhvcml0eTCB
34308 nzANBgkqhkiG9w0BAQEFAAOBjQAwgYkCgYEAwV2xWGcIYu6gmi0fCG2RFGiYCh7+2gRvE4RiIcPR
34309 fM6fBeC4AfBONOziipUEZKzxa1NfBbPLZ4C/QgKO/t0BCezhABRP/PvwDN1Dulsr4R+AcJkVV5MW
34310 8Q+XarfCaCMczE1ZMKxRHjuvK9buY0V7xdlfUNLjUA86iOe/FP3gx7kCAwEAAaOCAQkwggEFMHAG
34311 A1UdHwRpMGcwZaBjoGGkXzBdMQswCQYDVQQGEwJVUzEQMA4GA1UEChMHRXF1aWZheDEtMCsGA1UE
34312 CxMkRXF1aWZheCBTZWN1cmUgQ2VydGlmaWNhdGUgQXV0aG9yaXR5MQ0wCwYDVQQDEwRDUkwxMBoG
34313 A1UdEAQTMBGBDzIwMTgwODIyMTY0MTUxWjALBgNVHQ8EBAMCAQYwHwYDVR0jBBgwFoAUSOZo+SvS
34314 spXXR9gjIBBPM5iQn9QwHQYDVR0OBBYEFEjmaPkr0rKV10fYIyAQTzOYkJ/UMAwGA1UdEwQFMAMB
34315 Af8wGgYJKoZIhvZ9B0EABA0wCxsFVjMuMGMDAgbAMA0GCSqGSIb3DQEBBQUAA4GBAFjOKer89961
34316 zgK5F7WF0bnj4JXMJTENAKaSbn+2kmOeUJXRmm/kEd5jhW6Y7qj/WsjTVbJmcVfewCHrPSqnI0kB
34317 BIZCe/zuf6IWUrVnZ9NA2zsmWLIodz2uFHdh1voqZiegDfqnc1zqcPGUIWVEX/r87yloqaKHee95
34318 70+sB3c4
34319 -----END CERTIFICATE-----
34320
34321 Digital Signature Trust Co. Global CA 1
34322 =======================================
34323 -----BEGIN CERTIFICATE-----
34324 MIIDKTCCApKgAwIBAgIENnAVljANBgkqhkiG9w0BAQUFADBGMQswCQYDVQQGEwJVUzEkMCIGA1UE
34325 ChMbRGlnaXRhbCBTaWduYXR1cmUgVHJ1c3QgQ28uMREwDwYDVQQLEwhEU1RDQSBFMTAeFw05ODEy
34326 MTAxODEwMjNaFw0xODEyMTAxODQwMjNaMEYxCzAJBgNVBAYTAlVTMSQwIgYDVQQKExtEaWdpdGFs
34327 IFNpZ25hdHVyZSBUcnVzdCBDby4xETAPBgNVBAsTCERTVENBIEUxMIGdMA0GCSqGSIb3DQEBAQUA
34328 A4GLADCBhwKBgQCgbIGpzzQeJN3+hijM3oMv+V7UQtLodGBmE5gGHKlREmlvMVW5SXIACH7TpWJE
34329 NySZj9mDSI+ZbZUTu0M7LklOiDfBu1h//uG9+LthzfNHwJmm8fOR6Hh8AMthyUQncWlVSn5JTe2i
34330 o74CTADKAqjuAQIxZA9SLRN0dja1erQtcQIBA6OCASQwggEgMBEGCWCGSAGG+EIBAQQEAwIABzBo
34331 BgNVHR8EYTBfMF2gW6BZpFcwVTELMAkGA1UEBhMCVVMxJDAiBgNVBAoTG0RpZ2l0YWwgU2lnbmF0
34332 dXJlIFRydXN0IENvLjERMA8GA1UECxMIRFNUQ0EgRTExDTALBgNVBAMTBENSTDEwKwYDVR0QBCQw
34333 IoAPMTk5ODEyMTAxODEwMjNagQ8yMDE4MTIxMDE4MTAyM1owCwYDVR0PBAQDAgEGMB8GA1UdIwQY
34334 MBaAFGp5fpFpRhgTCgJ3pVlbYJglDqL4MB0GA1UdDgQWBBRqeX6RaUYYEwoCd6VZW2CYJQ6i+DAM
34335 BgNVHRMEBTADAQH/MBkGCSqGSIb2fQdBAAQMMAobBFY0LjADAgSQMA0GCSqGSIb3DQEBBQUAA4GB
34336 ACIS2Hod3IEGtgllsofIH160L+nEHvI8wbsEkBFKg05+k7lNQseSJqBcNJo4cvj9axY+IO6CizEq
34337 kzaFI4iKPANo08kJD038bKTaKHKTDomAsH3+gG9lbRgzl4vCa4nuYD3Im+9/KzJic5PLPON74nZ4
34338 RbyhkwS7hp86W0N6w4pl
34339 -----END CERTIFICATE-----
34340
34341 Digital Signature Trust Co. Global CA 3
34342 =======================================
34343 -----BEGIN CERTIFICATE-----
34344 MIIDKTCCApKgAwIBAgIENm7TzjANBgkqhkiG9w0BAQUFADBGMQswCQYDVQQGEwJVUzEkMCIGA1UE
34345 ChMbRGlnaXRhbCBTaWduYXR1cmUgVHJ1c3QgQ28uMREwDwYDVQQLEwhEU1RDQSBFMjAeFw05ODEy
34346 MDkxOTE3MjZaFw0xODEyMDkxOTQ3MjZaMEYxCzAJBgNVBAYTAlVTMSQwIgYDVQQKExtEaWdpdGFs
34347 IFNpZ25hdHVyZSBUcnVzdCBDby4xETAPBgNVBAsTCERTVENBIEUyMIGdMA0GCSqGSIb3DQEBAQUA
34348 A4GLADCBhwKBgQC/k48Xku8zExjrEH9OFr//Bo8qhbxe+SSmJIi2A7fBw18DW9Fvrn5C6mYjuGOD
34349 VvsoLeE4i7TuqAHhzhy2iCoiRoX7n6dwqUcUP87eZfCocfdPJmyMvMa1795JJ/9IKn3oTQPMx7JS
34350 xhcxEzu1TdvIxPbDDyQq2gyd55FbgM2UnQIBA6OCASQwggEgMBEGCWCGSAGG+EIBAQQEAwIABzBo
34351 BgNVHR8EYTBfMF2gW6BZpFcwVTELMAkGA1UEBhMCVVMxJDAiBgNVBAoTG0RpZ2l0YWwgU2lnbmF0
34352 dXJlIFRydXN0IENvLjERMA8GA1UECxMIRFNUQ0EgRTIxDTALBgNVBAMTBENSTDEwKwYDVR0QBCQw
34353 IoAPMTk5ODEyMDkxOTE3MjZagQ8yMDE4MTIwOTE5MTcyNlowCwYDVR0PBAQDAgEGMB8GA1UdIwQY
34354 MBaAFB6CTShlgDzJQW6sNS5ay97u+DlbMB0GA1UdDgQWBBQegk0oZYA8yUFurDUuWsve7vg5WzAM
34355 BgNVHRMEBTADAQH/MBkGCSqGSIb2fQdBAAQMMAobBFY0LjADAgSQMA0GCSqGSIb3DQEBBQUAA4GB
34356 AEeNg61i8tuwnkUiBbmi1gMOOHLnnvx75pO2mqWilMg0HZHRxdf0CiUPPXiBng+xZ8SQTGPdXqfi
34357 up/1902lMXucKS1M/mQ+7LZT/uqb7YLbdHVLB3luHtgZg3Pe9T7Qtd7nS2h9Qy4qIOF+oHhEngj1
34358 mPnHfxsb1gYgAlihw6ID
34359 -----END CERTIFICATE-----
34360
34361 Verisign Class 3 Public Primary Certification Authority
34362 =======================================================
34363 -----BEGIN CERTIFICATE-----
34364 MIICPDCCAaUCEHC65B0Q2Sk0tjjKewPMur8wDQYJKoZIhvcNAQECBQAwXzELMAkGA1UEBhMCVVMx
34365 FzAVBgNVBAoTDlZlcmlTaWduLCBJbmMuMTcwNQYDVQQLEy5DbGFzcyAzIFB1YmxpYyBQcmltYXJ5
34366 IENlcnRpZmljYXRpb24gQXV0aG9yaXR5MB4XDTk2MDEyOTAwMDAwMFoXDTI4MDgwMTIzNTk1OVow
34367 XzELMAkGA1UEBhMCVVMxFzAVBgNVBAoTDlZlcmlTaWduLCBJbmMuMTcwNQYDVQQLEy5DbGFzcyAz
34368 IFB1YmxpYyBQcmltYXJ5IENlcnRpZmljYXRpb24gQXV0aG9yaXR5MIGfMA0GCSqGSIb3DQEBAQUA
34369 A4GNADCBiQKBgQDJXFme8huKARS0EN8EQNvjV69qRUCPhAwL0TPZ2RHP7gJYHyX3KqhEBarsAx94
34370 f56TuZoAqiN91qyFomNFx3InzPRMxnVx0jnvT0Lwdd8KkMaOIG+YD/isI19wKTakyYbnsZogy1Ol
34371 hec9vn2a/iRFM9x2Fe0PonFkTGUugWhFpwIDAQABMA0GCSqGSIb3DQEBAgUAA4GBALtMEivPLCYA
34372 TxQT3ab7/AoRhIzzKBxnki98tsX63/Dolbwdj2wsqFHMc9ikwFPwTtYmwHYBV4GSXiHx0bH/59Ah
34373 WM1pF+NEHJwZRDmJXNycAA9WjQKZ7aKQRUzkuxCkPfAyAw7xzvjoyVGM5mKf5p/AfbdynMk2Omuf
34374 Tqj/ZA1k
34375 -----END CERTIFICATE-----
34376
34377 Verisign Class 1 Public Primary Certification Authority - G2
34378 ============================================================
34379 -----BEGIN CERTIFICATE-----
34380 MIIDAjCCAmsCEEzH6qqYPnHTkxD4PTqJkZIwDQYJKoZIhvcNAQEFBQAwgcExCzAJBgNVBAYTAlVT
34381 MRcwFQYDVQQKEw5WZXJpU2lnbiwgSW5jLjE8MDoGA1UECxMzQ2xhc3MgMSBQdWJsaWMgUHJpbWFy
34382 eSBDZXJ0aWZpY2F0aW9uIEF1dGhvcml0eSAtIEcyMTowOAYDVQQLEzEoYykgMTk5OCBWZXJpU2ln
34383 biwgSW5jLiAtIEZvciBhdXRob3JpemVkIHVzZSBvbmx5MR8wHQYDVQQLExZWZXJpU2lnbiBUcnVz
34384 dCBOZXR3b3JrMB4XDTk4MDUxODAwMDAwMFoXDTI4MDgwMTIzNTk1OVowgcExCzAJBgNVBAYTAlVT
34385 MRcwFQYDVQQKEw5WZXJpU2lnbiwgSW5jLjE8MDoGA1UECxMzQ2xhc3MgMSBQdWJsaWMgUHJpbWFy
34386 eSBDZXJ0aWZpY2F0aW9uIEF1dGhvcml0eSAtIEcyMTowOAYDVQQLEzEoYykgMTk5OCBWZXJpU2ln
34387 biwgSW5jLiAtIEZvciBhdXRob3JpemVkIHVzZSBvbmx5MR8wHQYDVQQLExZWZXJpU2lnbiBUcnVz
34388 dCBOZXR3b3JrMIGfMA0GCSqGSIb3DQEBAQUAA4GNADCBiQKBgQCq0Lq+Fi24g9TK0g+8djHKlNgd
34389 k4xWArzZbxpvUjZudVYKVdPfQ4chEWWKfo+9Id5rMj8bhDSVBZ1BNeuS65bdqlk/AVNtmU/t5eIq
34390 WpDBucSmFc/IReumXY6cPvBkJHalzasab7bYe1FhbqZ/h8jit+U03EGI6glAvnOSPWvndQIDAQAB
34391 MA0GCSqGSIb3DQEBBQUAA4GBAKlPww3HZ74sy9mozS11534Vnjty637rXC0Jh9ZrbWB85a7FkCMM
34392 XErQr7Fd88e2CtvgFZMN3QO8x3aKtd1Pw5sTdbgBwObJW2uluIncrKTdcu1OofdPvAbT6shkdHvC
34393 lUGcZXNY8ZCaPGqxmMnEh7zPRW1F4m4iP/68DzFc6PLZ
34394 -----END CERTIFICATE-----
34395
34396 Verisign Class 2 Public Primary Certification Authority - G2
34397 ============================================================
34398 -----BEGIN CERTIFICATE-----
34399 MIIDAzCCAmwCEQC5L2DMiJ+hekYJuFtwbIqvMA0GCSqGSIb3DQEBBQUAMIHBMQswCQYDVQQGEwJV
34400 UzEXMBUGA1UEChMOVmVyaVNpZ24sIEluYy4xPDA6BgNVBAsTM0NsYXNzIDIgUHVibGljIFByaW1h
34401 cnkgQ2VydGlmaWNhdGlvbiBBdXRob3JpdHkgLSBHMjE6MDgGA1UECxMxKGMpIDE5OTggVmVyaVNp
34402 Z24sIEluYy4gLSBGb3IgYXV0aG9yaXplZCB1c2Ugb25seTEfMB0GA1UECxMWVmVyaVNpZ24gVHJ1
34403 c3QgTmV0d29yazAeFw05ODA1MTgwMDAwMDBaFw0yODA4MDEyMzU5NTlaMIHBMQswCQYDVQQGEwJV
34404 UzEXMBUGA1UEChMOVmVyaVNpZ24sIEluYy4xPDA6BgNVBAsTM0NsYXNzIDIgUHVibGljIFByaW1h
34405 cnkgQ2VydGlmaWNhdGlvbiBBdXRob3JpdHkgLSBHMjE6MDgGA1UECxMxKGMpIDE5OTggVmVyaVNp
34406 Z24sIEluYy4gLSBGb3IgYXV0aG9yaXplZCB1c2Ugb25seTEfMB0GA1UECxMWVmVyaVNpZ24gVHJ1
34407 c3QgTmV0d29yazCBnzANBgkqhkiG9w0BAQEFAAOBjQAwgYkCgYEAp4gBIXQs5xoD8JjhlzwPIQjx
34408 nNuX6Zr8wgQGE75fUsjMHiwSViy4AWkszJkfrbCWrnkE8hM5wXuYuggs6MKEEyyqaekJ9MepAqRC
34409 wiNPStjwDqL7MWzJ5m+ZJwf15vRMeJ5t60aG+rmGyVTyssSv1EYcWskVMP8NbPUtDm3Of3cCAwEA
34410 ATANBgkqhkiG9w0BAQUFAAOBgQByLvl/0fFx+8Se9sVeUYpAmLho+Jscg9jinb3/7aHmZuovCfTK
34411 1+qlK5X2JGCGTUQug6XELaDTrnhpb3LabK4I8GOSN+a7xDAXrXfMSTWqz9iP0b63GJZHc2pUIjRk
34412 LbYWm1lbtFFZOrMLFPQS32eg9K0yZF6xRnInjBJ7xUS0rg==
34413 -----END CERTIFICATE-----
34414
34415 Verisign Class 3 Public Primary Certification Authority - G2
34416 ============================================================
34417 -----BEGIN CERTIFICATE-----
34418 MIIDAjCCAmsCEH3Z/gfPqB63EHln+6eJNMYwDQYJKoZIhvcNAQEFBQAwgcExCzAJBgNVBAYTAlVT
34419 MRcwFQYDVQQKEw5WZXJpU2lnbiwgSW5jLjE8MDoGA1UECxMzQ2xhc3MgMyBQdWJsaWMgUHJpbWFy
34420 eSBDZXJ0aWZpY2F0aW9uIEF1dGhvcml0eSAtIEcyMTowOAYDVQQLEzEoYykgMTk5OCBWZXJpU2ln
34421 biwgSW5jLiAtIEZvciBhdXRob3JpemVkIHVzZSBvbmx5MR8wHQYDVQQLExZWZXJpU2lnbiBUcnVz
34422 dCBOZXR3b3JrMB4XDTk4MDUxODAwMDAwMFoXDTI4MDgwMTIzNTk1OVowgcExCzAJBgNVBAYTAlVT
34423 MRcwFQYDVQQKEw5WZXJpU2lnbiwgSW5jLjE8MDoGA1UECxMzQ2xhc3MgMyBQdWJsaWMgUHJpbWFy
34424 eSBDZXJ0aWZpY2F0aW9uIEF1dGhvcml0eSAtIEcyMTowOAYDVQQLEzEoYykgMTk5OCBWZXJpU2ln
34425 biwgSW5jLiAtIEZvciBhdXRob3JpemVkIHVzZSBvbmx5MR8wHQYDVQQLExZWZXJpU2lnbiBUcnVz
34426 dCBOZXR3b3JrMIGfMA0GCSqGSIb3DQEBAQUAA4GNADCBiQKBgQDMXtERXVxp0KvTuWpMmR9ZmDCO
34427 FoUgRm1HP9SFIIThbbP4pO0M8RcPO/mn+SXXwc+EY/J8Y8+iR/LGWzOOZEAEaMGAuWQcRXfH2G71
34428 lSk8UOg013gfqLptQ5GVj0VXXn7F+8qkBOvqlzdUMG+7AUcyM83cV5tkaWH4mx0ciU9cZwIDAQAB
34429 MA0GCSqGSIb3DQEBBQUAA4GBAFFNzb5cy5gZnBWyATl4Lk0PZ3BwmcYQWpSkU01UbSuvDV1Ai2TT
34430 1+7eVmGSX6bEHRBhNtMsJzzoKQm5EWR0zLVznxxIqbxhAe7iF6YM40AIOw7n60RzKprxaZLvcRTD
34431 Oaxxp5EJb+RxBrO6WVcmeQD2+A2iMzAo1KpYoJ2daZH9
34432 -----END CERTIFICATE-----
34433
34434 GlobalSign Root CA
34435 ==================
34436 -----BEGIN CERTIFICATE-----
34437 MIIDdTCCAl2gAwIBAgILBAAAAAABFUtaw5QwDQYJKoZIhvcNAQEFBQAwVzELMAkGA1UEBhMCQkUx
34438 GTAXBgNVBAoTEEdsb2JhbFNpZ24gbnYtc2ExEDAOBgNVBAsTB1Jvb3QgQ0ExGzAZBgNVBAMTEkds
34439 b2JhbFNpZ24gUm9vdCBDQTAeFw05ODA5MDExMjAwMDBaFw0yODAxMjgxMjAwMDBaMFcxCzAJBgNV
34440 BAYTAkJFMRkwFwYDVQQKExBHbG9iYWxTaWduIG52LXNhMRAwDgYDVQQLEwdSb290IENBMRswGQYD
34441 VQQDExJHbG9iYWxTaWduIFJvb3QgQ0EwggEiMA0GCSqGSIb3DQEBAQUAA4IBDwAwggEKAoIBAQDa
34442 DuaZjc6j40+Kfvvxi4Mla+pIH/EqsLmVEQS98GPR4mdmzxzdzxtIK+6NiY6arymAZavpxy0Sy6sc
34443 THAHoT0KMM0VjU/43dSMUBUc71DuxC73/OlS8pF94G3VNTCOXkNz8kHp1Wrjsok6Vjk4bwY8iGlb
34444 Kk3Fp1S4bInMm/k8yuX9ifUSPJJ4ltbcdG6TRGHRjcdGsnUOhugZitVtbNV4FpWi6cgKOOvyJBNP
34445 c1STE4U6G7weNLWLBYy5d4ux2x8gkasJU26Qzns3dLlwR5EiUWMWea6xrkEmCMgZK9FGqkjWZCrX
34446 gzT/LCrBbBlDSgeF59N89iFo7+ryUp9/k5DPAgMBAAGjQjBAMA4GA1UdDwEB/wQEAwIBBjAPBgNV
34447 HRMBAf8EBTADAQH/MB0GA1UdDgQWBBRge2YaRQ2XyolQL30EzTSo//z9SzANBgkqhkiG9w0BAQUF
34448 AAOCAQEA1nPnfE920I2/7LqivjTFKDK1fPxsnCwrvQmeU79rXqoRSLblCKOzyj1hTdNGCbM+w6Dj
34449 Y1Ub8rrvrTnhQ7k4o+YviiY776BQVvnGCv04zcQLcFGUl5gE38NflNUVyRRBnMRddWQVDf9VMOyG
34450 j/8N7yy5Y0b2qvzfvGn9LhJIZJrglfCm7ymPAbEVtQwdpf5pLGkkeB6zpxxxYu7KyJesF12KwvhH
34451 hm4qxFYxldBniYUr+WymXUadDKqC5JlR3XC321Y9YeRq4VzW9v493kHMB65jUr9TU/Qr6cf9tveC
34452 X4XSQRjbgbMEHMUfpIBvFSDJ3gyICh3WZlXi/EjJKSZp4A==
34453 -----END CERTIFICATE-----
34454
34455 GlobalSign Root CA - R2
34456 =======================
34457 -----BEGIN CERTIFICATE-----
34458 MIIDujCCAqKgAwIBAgILBAAAAAABD4Ym5g0wDQYJKoZIhvcNAQEFBQAwTDEgMB4GA1UECxMXR2xv
34459 YmFsU2lnbiBSb290IENBIC0gUjIxEzARBgNVBAoTCkdsb2JhbFNpZ24xEzARBgNVBAMTCkdsb2Jh
34460 bFNpZ24wHhcNMDYxMjE1MDgwMDAwWhcNMjExMjE1MDgwMDAwWjBMMSAwHgYDVQQLExdHbG9iYWxT
34461 aWduIFJvb3QgQ0EgLSBSMjETMBEGA1UEChMKR2xvYmFsU2lnbjETMBEGA1UEAxMKR2xvYmFsU2ln
34462 bjCCASIwDQYJKoZIhvcNAQEBBQADggEPADCCAQoCggEBAKbPJA6+Lm8omUVCxKs+IVSbC9N/hHD6
34463 ErPLv4dfxn+G07IwXNb9rfF73OX4YJYJkhD10FPe+3t+c4isUoh7SqbKSaZeqKeMWhG8eoLrvozp
34464 s6yWJQeXSpkqBy+0Hne/ig+1AnwblrjFuTosvNYSuetZfeLQBoZfXklqtTleiDTsvHgMCJiEbKjN
34465 S7SgfQx5TfC4LcshytVsW33hoCmEofnTlEnLJGKRILzdC9XZzPnqJworc5HGnRusyMvo4KD0L5CL
34466 TfuwNhv2GXqF4G3yYROIXJ/gkwpRl4pazq+r1feqCapgvdzZX99yqWATXgAByUr6P6TqBwMhAo6C
34467 ygPCm48CAwEAAaOBnDCBmTAOBgNVHQ8BAf8EBAMCAQYwDwYDVR0TAQH/BAUwAwEB/zAdBgNVHQ4E
34468 FgQUm+IHV2ccHsBqBt5ZtJot39wZhi4wNgYDVR0fBC8wLTAroCmgJ4YlaHR0cDovL2NybC5nbG9i
34469 YWxzaWduLm5ldC9yb290LXIyLmNybDAfBgNVHSMEGDAWgBSb4gdXZxwewGoG3lm0mi3f3BmGLjAN
34470 BgkqhkiG9w0BAQUFAAOCAQEAmYFThxxol4aR7OBKuEQLq4GsJ0/WwbgcQ3izDJr86iw8bmEbTUsp
34471 9Z8FHSbBuOmDAGJFtqkIk7mpM0sYmsL4h4hO291xNBrBVNpGP+DTKqttVCL1OmLNIG+6KYnX3ZHu
34472 01yiPqFbQfXf5WRDLenVOavSot+3i9DAgBkcRcAtjOj4LaR0VknFBbVPFd5uRHg5h6h+u/N5GJG7
34473 9G+dwfCMNYxdAfvDbbnvRG15RjF+Cv6pgsH/76tuIMRQyV+dTZsXjAzlAcmgQWpzU/qlULRuJQ/7
34474 TBj0/VLZjmmx6BEP3ojY+x1J96relc8geMJgEtslQIxq/H5COEBkEveegeGTLg==
34475 -----END CERTIFICATE-----
34476
34477 ValiCert Class 1 VA
34478 ===================
34479 -----BEGIN CERTIFICATE-----
34480 MIIC5zCCAlACAQEwDQYJKoZIhvcNAQEFBQAwgbsxJDAiBgNVBAcTG1ZhbGlDZXJ0IFZhbGlkYXRp
34481 b24gTmV0d29yazEXMBUGA1UEChMOVmFsaUNlcnQsIEluYy4xNTAzBgNVBAsTLFZhbGlDZXJ0IENs
34482 YXNzIDEgUG9saWN5IFZhbGlkYXRpb24gQXV0aG9yaXR5MSEwHwYDVQQDExhodHRwOi8vd3d3LnZh
34483 bGljZXJ0LmNvbS8xIDAeBgkqhkiG9w0BCQEWEWluZm9AdmFsaWNlcnQuY29tMB4XDTk5MDYyNTIy
34484 MjM0OFoXDTE5MDYyNTIyMjM0OFowgbsxJDAiBgNVBAcTG1ZhbGlDZXJ0IFZhbGlkYXRpb24gTmV0
34485 d29yazEXMBUGA1UEChMOVmFsaUNlcnQsIEluYy4xNTAzBgNVBAsTLFZhbGlDZXJ0IENsYXNzIDEg
34486 UG9saWN5IFZhbGlkYXRpb24gQXV0aG9yaXR5MSEwHwYDVQQDExhodHRwOi8vd3d3LnZhbGljZXJ0
34487 LmNvbS8xIDAeBgkqhkiG9w0BCQEWEWluZm9AdmFsaWNlcnQuY29tMIGfMA0GCSqGSIb3DQEBAQUA
34488 A4GNADCBiQKBgQDYWYJ6ibiWuqYvaG9YLqdUHAZu9OqNSLwxlBfw8068srg1knaw0KWlAdcAAxIi
34489 GQj4/xEjm84H9b9pGib+TunRf50sQB1ZaG6m+FiwnRqP0z/x3BkGgagO4DrdyFNFCQbmD3DD+kCm
34490 DuJWBQ8YTfwggtFzVXSNdnKgHZ0dwN0/cQIDAQABMA0GCSqGSIb3DQEBBQUAA4GBAFBoPUn0LBwG
34491 lN+VYH+Wexf+T3GtZMjdd9LvWVXoP+iOBSoh8gfStadS/pyxtuJbdxdA6nLWI8sogTLDAHkY7FkX
34492 icnGah5xyf23dKUlRWnFSKsZ4UWKJWsZ7uW7EvV/96aNUcPwnXS3qT6gpf+2SQMT2iLM7XGCK5nP
34493 Orf1LXLI
34494 -----END CERTIFICATE-----
34495
34496 ValiCert Class 2 VA
34497 ===================
34498 -----BEGIN CERTIFICATE-----
34499 MIIC5zCCAlACAQEwDQYJKoZIhvcNAQEFBQAwgbsxJDAiBgNVBAcTG1ZhbGlDZXJ0IFZhbGlkYXRp
34500 b24gTmV0d29yazEXMBUGA1UEChMOVmFsaUNlcnQsIEluYy4xNTAzBgNVBAsTLFZhbGlDZXJ0IENs
34501 YXNzIDIgUG9saWN5IFZhbGlkYXRpb24gQXV0aG9yaXR5MSEwHwYDVQQDExhodHRwOi8vd3d3LnZh
34502 bGljZXJ0LmNvbS8xIDAeBgkqhkiG9w0BCQEWEWluZm9AdmFsaWNlcnQuY29tMB4XDTk5MDYyNjAw
34503 MTk1NFoXDTE5MDYyNjAwMTk1NFowgbsxJDAiBgNVBAcTG1ZhbGlDZXJ0IFZhbGlkYXRpb24gTmV0
34504 d29yazEXMBUGA1UEChMOVmFsaUNlcnQsIEluYy4xNTAzBgNVBAsTLFZhbGlDZXJ0IENsYXNzIDIg
34505 UG9saWN5IFZhbGlkYXRpb24gQXV0aG9yaXR5MSEwHwYDVQQDExhodHRwOi8vd3d3LnZhbGljZXJ0
34506 LmNvbS8xIDAeBgkqhkiG9w0BCQEWEWluZm9AdmFsaWNlcnQuY29tMIGfMA0GCSqGSIb3DQEBAQUA
34507 A4GNADCBiQKBgQDOOnHK5avIWZJV16vYdA757tn2VUdZZUcOBVXc65g2PFxTXdMwzzjsvUGJ7SVC
34508 CSRrCl6zfN1SLUzm1NZ9WlmpZdRJEy0kTRxQb7XBhVQ7/nHk01xC+YDgkRoKWzk2Z/M/VXwbP7Rf
34509 ZHM047QSv4dk+NoS/zcnwbNDu+97bi5p9wIDAQABMA0GCSqGSIb3DQEBBQUAA4GBADt/UG9vUJSZ
34510 SWI4OB9L+KXIPqeCgfYrx+jFzug6EILLGACOTb2oWH+heQC1u+mNr0HZDzTuIYEZoDJJKPTEjlbV
34511 UjP9UNV+mWwD5MlM/Mtsq2azSiGM5bUMMj4QssxsodyamEwCW/POuZ6lcg5Ktz885hZo+L7tdEy8
34512 W9ViH0Pd
34513 -----END CERTIFICATE-----
34514
34515 RSA Root Certificate 1
34516 ======================
34517 -----BEGIN CERTIFICATE-----
34518 MIIC5zCCAlACAQEwDQYJKoZIhvcNAQEFBQAwgbsxJDAiBgNVBAcTG1ZhbGlDZXJ0IFZhbGlkYXRp
34519 b24gTmV0d29yazEXMBUGA1UEChMOVmFsaUNlcnQsIEluYy4xNTAzBgNVBAsTLFZhbGlDZXJ0IENs
34520 YXNzIDMgUG9saWN5IFZhbGlkYXRpb24gQXV0aG9yaXR5MSEwHwYDVQQDExhodHRwOi8vd3d3LnZh
34521 bGljZXJ0LmNvbS8xIDAeBgkqhkiG9w0BCQEWEWluZm9AdmFsaWNlcnQuY29tMB4XDTk5MDYyNjAw
34522 MjIzM1oXDTE5MDYyNjAwMjIzM1owgbsxJDAiBgNVBAcTG1ZhbGlDZXJ0IFZhbGlkYXRpb24gTmV0
34523 d29yazEXMBUGA1UEChMOVmFsaUNlcnQsIEluYy4xNTAzBgNVBAsTLFZhbGlDZXJ0IENsYXNzIDMg
34524 UG9saWN5IFZhbGlkYXRpb24gQXV0aG9yaXR5MSEwHwYDVQQDExhodHRwOi8vd3d3LnZhbGljZXJ0
34525 LmNvbS8xIDAeBgkqhkiG9w0BCQEWEWluZm9AdmFsaWNlcnQuY29tMIGfMA0GCSqGSIb3DQEBAQUA
34526 A4GNADCBiQKBgQDjmFGWHOjVsQaBalfDcnWTq8+epvzzFlLWLU2fNUSoLgRNB0mKOCn1dzfnt6td
34527 3zZxFJmP3MKS8edgkpfs2Ejcv8ECIMYkpChMMFp2bbFc893enhBxoYjHW5tBbcqwuI4V7q0zK89H
34528 BFx1cQqYJJgpp0lZpd34t0NiYfPT4tBVPwIDAQABMA0GCSqGSIb3DQEBBQUAA4GBAFa7AliEZwgs
34529 3x/be0kz9dNnnfS0ChCzycUs4pJqcXgn8nCDQtM+z6lU9PHYkhaM0QTLS6vJn0WuPIqpsHEzXcjF
34530 V9+vqDWzf4mH6eglkrh/hXqu1rweN1gqZ8mRzyqBPu3GOd/APhmcGcwTTYJBtYze4D1gCCAPRX5r
34531 on+jjBXu
34532 -----END CERTIFICATE-----
34533
34534 Verisign Class 1 Public Primary Certification Authority - G3
34535 ============================================================
34536 -----BEGIN CERTIFICATE-----
34537 MIIEGjCCAwICEQCLW3VWhFSFCwDPrzhIzrGkMA0GCSqGSIb3DQEBBQUAMIHKMQswCQYDVQQGEwJV
34538 UzEXMBUGA1UEChMOVmVyaVNpZ24sIEluYy4xHzAdBgNVBAsTFlZlcmlTaWduIFRydXN0IE5ldHdv
34539 cmsxOjA4BgNVBAsTMShjKSAxOTk5IFZlcmlTaWduLCBJbmMuIC0gRm9yIGF1dGhvcml6ZWQgdXNl
34540 IG9ubHkxRTBDBgNVBAMTPFZlcmlTaWduIENsYXNzIDEgUHVibGljIFByaW1hcnkgQ2VydGlmaWNh
34541 dGlvbiBBdXRob3JpdHkgLSBHMzAeFw05OTEwMDEwMDAwMDBaFw0zNjA3MTYyMzU5NTlaMIHKMQsw
34542 CQYDVQQGEwJVUzEXMBUGA1UEChMOVmVyaVNpZ24sIEluYy4xHzAdBgNVBAsTFlZlcmlTaWduIFRy
34543 dXN0IE5ldHdvcmsxOjA4BgNVBAsTMShjKSAxOTk5IFZlcmlTaWduLCBJbmMuIC0gRm9yIGF1dGhv
34544 cml6ZWQgdXNlIG9ubHkxRTBDBgNVBAMTPFZlcmlTaWduIENsYXNzIDEgUHVibGljIFByaW1hcnkg
34545 Q2VydGlmaWNhdGlvbiBBdXRob3JpdHkgLSBHMzCCASIwDQYJKoZIhvcNAQEBBQADggEPADCCAQoC
34546 ggEBAN2E1Lm0+afY8wR4nN493GwTFtl63SRRZsDHJlkNrAYIwpTRMx/wgzUfbhvI3qpuFU5UJ+/E
34547 bRrsC+MO8ESlV8dAWB6jRx9x7GD2bZTIGDnt/kIYVt/kTEkQeE4BdjVjEjbdZrwBBDajVWjVojYJ
34548 rKshJlQGrT/KFOCsyq0GHZXi+J3x4GD/wn91K0zM2v6HmSHquv4+VNfSWXjbPG7PoBMAGrgnoeS+
34549 Z5bKoMWznN3JdZ7rMJpfo83ZrngZPyPpXNspva1VyBtUjGP26KbqxzcSXKMpHgLZ2x87tNcPVkeB
34550 FQRKr4Mn0cVYiMHd9qqnoxjaaKptEVHhv2Vrn5Z20T0CAwEAATANBgkqhkiG9w0BAQUFAAOCAQEA
34551 q2aN17O6x5q25lXQBfGfMY1aqtmqRiYPce2lrVNWYgFHKkTp/j90CxObufRNG7LRX7K20ohcs5/N
34552 y9Sn2WCVhDr4wTcdYcrnsMXlkdpUpqwxga6X3s0IrLjAl4B/bnKk52kTlWUfxJM8/XmPBNQ+T+r3
34553 ns7NZ3xPZQL/kYVUc8f/NveGLezQXk//EZ9yBta4GvFMDSZl4kSAHsef493oCtrspSCAaWihT37h
34554 a88HQfqDjrw43bAuEbFrskLMmrz5SCJ5ShkPshw+IHTZasO+8ih4E1Z5T21Q6huwtVexN2ZYI/Pc
34555 D98Kh8TvhgXVOBRgmaNL3gaWcSzy27YfpO8/7g==
34556 -----END CERTIFICATE-----
34557
34558 Verisign Class 2 Public Primary Certification Authority - G3
34559 ============================================================
34560 -----BEGIN CERTIFICATE-----
34561 MIIEGTCCAwECEGFwy0mMX5hFKeewptlQW3owDQYJKoZIhvcNAQEFBQAwgcoxCzAJBgNVBAYTAlVT
34562 MRcwFQYDVQQKEw5WZXJpU2lnbiwgSW5jLjEfMB0GA1UECxMWVmVyaVNpZ24gVHJ1c3QgTmV0d29y
34563 azE6MDgGA1UECxMxKGMpIDE5OTkgVmVyaVNpZ24sIEluYy4gLSBGb3IgYXV0aG9yaXplZCB1c2Ug
34564 b25seTFFMEMGA1UEAxM8VmVyaVNpZ24gQ2xhc3MgMiBQdWJsaWMgUHJpbWFyeSBDZXJ0aWZpY2F0
34565 aW9uIEF1dGhvcml0eSAtIEczMB4XDTk5MTAwMTAwMDAwMFoXDTM2MDcxNjIzNTk1OVowgcoxCzAJ
34566 BgNVBAYTAlVTMRcwFQYDVQQKEw5WZXJpU2lnbiwgSW5jLjEfMB0GA1UECxMWVmVyaVNpZ24gVHJ1
34567 c3QgTmV0d29yazE6MDgGA1UECxMxKGMpIDE5OTkgVmVyaVNpZ24sIEluYy4gLSBGb3IgYXV0aG9y
34568 aXplZCB1c2Ugb25seTFFMEMGA1UEAxM8VmVyaVNpZ24gQ2xhc3MgMiBQdWJsaWMgUHJpbWFyeSBD
34569 ZXJ0aWZpY2F0aW9uIEF1dGhvcml0eSAtIEczMIIBIjANBgkqhkiG9w0BAQEFAAOCAQ8AMIIBCgKC
34570 AQEArwoNwtUs22e5LeWUJ92lvuCwTY+zYVY81nzD9M0+hsuiiOLh2KRpxbXiv8GmR1BeRjmL1Za6
34571 tW8UvxDOJxOeBUebMXoT2B/Z0wI3i60sR/COgQanDTAM6/c8DyAd3HJG7qUCyFvDyVZpTMUYwZF7
34572 C9UTAJu878NIPkZgIIUq1ZC2zYugzDLdt/1AVbJQHFauzI13TccgTacxdu9okoqQHgiBVrKtaaNS
34573 0MscxCM9H5n+TOgWY47GCI72MfbS+uV23bUckqNJzc0BzWjNqWm6o+sdDZykIKbBoMXRRkwXbdKs
34574 Zj+WjOCE1Db/IlnF+RFgqF8EffIa9iVCYQ/ESrg+iQIDAQABMA0GCSqGSIb3DQEBBQUAA4IBAQA0
34575 JhU8wI1NQ0kdvekhktdmnLfexbjQ5F1fdiLAJvmEOjr5jLX77GDx6M4EsMjdpwOPMPOY36TmpDHf
34576 0xwLRtxyID+u7gU8pDM/CzmscHhzS5kr3zDCVLCoO1Wh/hYozUK9dG6A2ydEp85EXdQbkJgNHkKU
34577 sQAsBNB0owIFImNjzYO1+8FtYmtpdf1dcEG59b98377BMnMiIYtYgXsVkXq642RIsH/7NiXaldDx
34578 JBQX3RiAa0YjOVT1jmIJBB2UkKab5iXiQkWquJCtvgiPqQtCGJTPcjnhsUPgKM+351psE2tJs//j
34579 GHyJizNdrDPXp/naOlXJWBD5qu9ats9LS98q
34580 -----END CERTIFICATE-----
34581
34582 Verisign Class 3 Public Primary Certification Authority - G3
34583 ============================================================
34584 -----BEGIN CERTIFICATE-----
34585 MIIEGjCCAwICEQCbfgZJoz5iudXukEhxKe9XMA0GCSqGSIb3DQEBBQUAMIHKMQswCQYDVQQGEwJV
34586 UzEXMBUGA1UEChMOVmVyaVNpZ24sIEluYy4xHzAdBgNVBAsTFlZlcmlTaWduIFRydXN0IE5ldHdv
34587 cmsxOjA4BgNVBAsTMShjKSAxOTk5IFZlcmlTaWduLCBJbmMuIC0gRm9yIGF1dGhvcml6ZWQgdXNl
34588 IG9ubHkxRTBDBgNVBAMTPFZlcmlTaWduIENsYXNzIDMgUHVibGljIFByaW1hcnkgQ2VydGlmaWNh
34589 dGlvbiBBdXRob3JpdHkgLSBHMzAeFw05OTEwMDEwMDAwMDBaFw0zNjA3MTYyMzU5NTlaMIHKMQsw
34590 CQYDVQQGEwJVUzEXMBUGA1UEChMOVmVyaVNpZ24sIEluYy4xHzAdBgNVBAsTFlZlcmlTaWduIFRy
34591 dXN0IE5ldHdvcmsxOjA4BgNVBAsTMShjKSAxOTk5IFZlcmlTaWduLCBJbmMuIC0gRm9yIGF1dGhv
34592 cml6ZWQgdXNlIG9ubHkxRTBDBgNVBAMTPFZlcmlTaWduIENsYXNzIDMgUHVibGljIFByaW1hcnkg
34593 Q2VydGlmaWNhdGlvbiBBdXRob3JpdHkgLSBHMzCCASIwDQYJKoZIhvcNAQEBBQADggEPADCCAQoC
34594 ggEBAMu6nFL8eB8aHm8bN3O9+MlrlBIwT/A2R/XQkQr1F8ilYcEWQE37imGQ5XYgwREGfassbqb1
34595 EUGO+i2tKmFZpGcmTNDovFJbcCAEWNF6yaRpvIMXZK0Fi7zQWM6NjPXr8EJJC52XJ2cybuGukxUc
34596 cLwgTS8Y3pKI6GyFVxEa6X7jJhFUokWWVYPKMIno3Nij7SqAP395ZVc+FSBmCC+Vk7+qRy+oRpfw
34597 EuL+wgorUeZ25rdGt+INpsyow0xZVYnm6FNcHOqd8GIWC6fJXwzw3sJ2zq/3avL6QaaiMxTJ5Xpj
34598 055iN9WFZZ4O5lMkdBteHRJTW8cs54NJOxWuimi5V5cCAwEAATANBgkqhkiG9w0BAQUFAAOCAQEA
34599 ERSWwauSCPc/L8my/uRan2Te2yFPhpk0djZX3dAVL8WtfxUfN2JzPtTnX84XA9s1+ivbrmAJXx5f
34600 j267Cz3qWhMeDGBvtcC1IyIuBwvLqXTLR7sdwdela8wv0kL9Sd2nic9TutoAWii/gt/4uhMdUIaC
34601 /Y4wjylGsB49Ndo4YhYYSq3mtlFs3q9i6wHQHiT+eo8SGhJouPtmmRQURVyu565pF4ErWjfJXir0
34602 xuKhXFSbplQAz/DxwceYMBo7Nhbbo27q/a2ywtrvAkcTisDxszGtTxzhT5yvDwyd93gN2PQ1VoDa
34603 t20Xj50egWTh/sVFuq1ruQp6Tk9LhO5L8X3dEQ==
34604 -----END CERTIFICATE-----
34605
34606 Verisign Class 4 Public Primary Certification Authority - G3
34607 ============================================================
34608 -----BEGIN CERTIFICATE-----
34609 MIIEGjCCAwICEQDsoKeLbnVqAc/EfMwvlF7XMA0GCSqGSIb3DQEBBQUAMIHKMQswCQYDVQQGEwJV
34610 UzEXMBUGA1UEChMOVmVyaVNpZ24sIEluYy4xHzAdBgNVBAsTFlZlcmlTaWduIFRydXN0IE5ldHdv
34611 cmsxOjA4BgNVBAsTMShjKSAxOTk5IFZlcmlTaWduLCBJbmMuIC0gRm9yIGF1dGhvcml6ZWQgdXNl
34612 IG9ubHkxRTBDBgNVBAMTPFZlcmlTaWduIENsYXNzIDQgUHVibGljIFByaW1hcnkgQ2VydGlmaWNh
34613 dGlvbiBBdXRob3JpdHkgLSBHMzAeFw05OTEwMDEwMDAwMDBaFw0zNjA3MTYyMzU5NTlaMIHKMQsw
34614 CQYDVQQGEwJVUzEXMBUGA1UEChMOVmVyaVNpZ24sIEluYy4xHzAdBgNVBAsTFlZlcmlTaWduIFRy
34615 dXN0IE5ldHdvcmsxOjA4BgNVBAsTMShjKSAxOTk5IFZlcmlTaWduLCBJbmMuIC0gRm9yIGF1dGhv
34616 cml6ZWQgdXNlIG9ubHkxRTBDBgNVBAMTPFZlcmlTaWduIENsYXNzIDQgUHVibGljIFByaW1hcnkg
34617 Q2VydGlmaWNhdGlvbiBBdXRob3JpdHkgLSBHMzCCASIwDQYJKoZIhvcNAQEBBQADggEPADCCAQoC
34618 ggEBAK3LpRFpxlmr8Y+1GQ9Wzsy1HyDkniYlS+BzZYlZ3tCD5PUPtbut8XzoIfzk6AzufEUiGXaS
34619 tBO3IFsJ+mGuqPKljYXCKtbeZjbSmwL0qJJgfJxptI8kHtCGUvYynEFYHiK9zUVilQhu0GbdU6LM
34620 8BDcVHOLBKFGMzNcF0C5nk3T875Vg+ixiY5afJqWIpA7iCXy0lOIAgwLePLmNxdLMEYH5IBtptiW
34621 Lugs+BGzOA1mppvqySNb247i8xOOGlktqgLw7KSHZtzBP/XYufTsgsbSPZUd5cBPhMnZo0QoBmrX
34622 Razwa2rvTl/4EYIeOGM0ZlDUPpNz+jDDZq3/ky2X7wMCAwEAATANBgkqhkiG9w0BAQUFAAOCAQEA
34623 j/ola09b5KROJ1WrIhVZPMq1CtRK26vdoV9TxaBXOcLORyu+OshWv8LZJxA6sQU8wHcxuzrTBXtt
34624 mhwwjIDLk5Mqg6sFUYICABFna/OIYUdfA5PVWw3g8dShMjWFsjrbsIKr0csKvE+MW8VLADsfKoKm
34625 fjaF3H48ZwC15DtS4KjrXRX5xm3wrR0OhbepmnMUWluPQSjA1egtTaRezarZ7c7c2NU8Qh0XwRJd
34626 RTjDOPP8hS6DRkiy1yBfkjaP53kPmF6Z6PDQpLv1U70qzlmwr25/bLvSHgCwIe34QWKCudiyxLtG
34627 UPMxxY8BqHTr9Xgn2uf3ZkPznoM+IKrDNWCRzg==
34628 -----END CERTIFICATE-----
34629
34630 Entrust.net Secure Server CA
34631 ============================
34632 -----BEGIN CERTIFICATE-----
34633 MIIE2DCCBEGgAwIBAgIEN0rSQzANBgkqhkiG9w0BAQUFADCBwzELMAkGA1UEBhMCVVMxFDASBgNV
34634 BAoTC0VudHJ1c3QubmV0MTswOQYDVQQLEzJ3d3cuZW50cnVzdC5uZXQvQ1BTIGluY29ycC4gYnkg
34635 cmVmLiAobGltaXRzIGxpYWIuKTElMCMGA1UECxMcKGMpIDE5OTkgRW50cnVzdC5uZXQgTGltaXRl
34636 ZDE6MDgGA1UEAxMxRW50cnVzdC5uZXQgU2VjdXJlIFNlcnZlciBDZXJ0aWZpY2F0aW9uIEF1dGhv
34637 cml0eTAeFw05OTA1MjUxNjA5NDBaFw0xOTA1MjUxNjM5NDBaMIHDMQswCQYDVQQGEwJVUzEUMBIG
34638 A1UEChMLRW50cnVzdC5uZXQxOzA5BgNVBAsTMnd3dy5lbnRydXN0Lm5ldC9DUFMgaW5jb3JwLiBi
34639 eSByZWYuIChsaW1pdHMgbGlhYi4pMSUwIwYDVQQLExwoYykgMTk5OSBFbnRydXN0Lm5ldCBMaW1p
34640 dGVkMTowOAYDVQQDEzFFbnRydXN0Lm5ldCBTZWN1cmUgU2VydmVyIENlcnRpZmljYXRpb24gQXV0
34641 aG9yaXR5MIGdMA0GCSqGSIb3DQEBAQUAA4GLADCBhwKBgQDNKIM0VBuJ8w+vN5Ex/68xYMmo6LIQ
34642 aO2f55M28Qpku0f1BBc/I0dNxScZgSYMVHINiC3ZH5oSn7yzcdOAGT9HZnuMNSjSuQrfJNqc1lB5
34643 gXpa0zf3wkrYKZImZNHkmGw6AIr1NJtl+O3jEP/9uElY3KDegjlrgbEWGWG5VLbmQwIBA6OCAdcw
34644 ggHTMBEGCWCGSAGG+EIBAQQEAwIABzCCARkGA1UdHwSCARAwggEMMIHeoIHboIHYpIHVMIHSMQsw
34645 CQYDVQQGEwJVUzEUMBIGA1UEChMLRW50cnVzdC5uZXQxOzA5BgNVBAsTMnd3dy5lbnRydXN0Lm5l
34646 dC9DUFMgaW5jb3JwLiBieSByZWYuIChsaW1pdHMgbGlhYi4pMSUwIwYDVQQLExwoYykgMTk5OSBF
34647 bnRydXN0Lm5ldCBMaW1pdGVkMTowOAYDVQQDEzFFbnRydXN0Lm5ldCBTZWN1cmUgU2VydmVyIENl
34648 cnRpZmljYXRpb24gQXV0aG9yaXR5MQ0wCwYDVQQDEwRDUkwxMCmgJ6AlhiNodHRwOi8vd3d3LmVu
34649 dHJ1c3QubmV0L0NSTC9uZXQxLmNybDArBgNVHRAEJDAigA8xOTk5MDUyNTE2MDk0MFqBDzIwMTkw
34650 NTI1MTYwOTQwWjALBgNVHQ8EBAMCAQYwHwYDVR0jBBgwFoAU8BdiE1U9s/8KAGv7UISX8+1i0Bow
34651 HQYDVR0OBBYEFPAXYhNVPbP/CgBr+1CEl/PtYtAaMAwGA1UdEwQFMAMBAf8wGQYJKoZIhvZ9B0EA
34652 BAwwChsEVjQuMAMCBJAwDQYJKoZIhvcNAQEFBQADgYEAkNwwAvpkdMKnCqV8IY00F6j7Rw7/JXyN
34653 Ewr75Ji174z4xRAN95K+8cPV1ZVqBLssziY2ZcgxxufuP+NXdYR6Ee9GTxj005i7qIcyunL2POI9
34654 n9cd2cNgQ4xYDiKWL2KjLB+6rQXvqzJ4h6BUcxm1XAX5Uj5tLUUL9wqT6u0G+bI=
34655 -----END CERTIFICATE-----
34656
34657 Entrust.net Premium 2048 Secure Server CA
34658 =========================================
34659 -----BEGIN CERTIFICATE-----
34660 MIIEXDCCA0SgAwIBAgIEOGO5ZjANBgkqhkiG9w0BAQUFADCBtDEUMBIGA1UEChMLRW50cnVzdC5u
34661 ZXQxQDA+BgNVBAsUN3d3dy5lbnRydXN0Lm5ldC9DUFNfMjA0OCBpbmNvcnAuIGJ5IHJlZi4gKGxp
34662 bWl0cyBsaWFiLikxJTAjBgNVBAsTHChjKSAxOTk5IEVudHJ1c3QubmV0IExpbWl0ZWQxMzAxBgNV
34663 BAMTKkVudHJ1c3QubmV0IENlcnRpZmljYXRpb24gQXV0aG9yaXR5ICgyMDQ4KTAeFw05OTEyMjQx
34664 NzUwNTFaFw0xOTEyMjQxODIwNTFaMIG0MRQwEgYDVQQKEwtFbnRydXN0Lm5ldDFAMD4GA1UECxQ3
34665 d3d3LmVudHJ1c3QubmV0L0NQU18yMDQ4IGluY29ycC4gYnkgcmVmLiAobGltaXRzIGxpYWIuKTEl
34666 MCMGA1UECxMcKGMpIDE5OTkgRW50cnVzdC5uZXQgTGltaXRlZDEzMDEGA1UEAxMqRW50cnVzdC5u
34667 ZXQgQ2VydGlmaWNhdGlvbiBBdXRob3JpdHkgKDIwNDgpMIIBIjANBgkqhkiG9w0BAQEFAAOCAQ8A
34668 MIIBCgKCAQEArU1LqRKGsuqjIAcVFmQqK0vRvwtKTY7tgHalZ7d4QMBzQshowNtTK91euHaYNZOL
34669 Gp18EzoOH1u3Hs/lJBQesYGpjX24zGtLA/ECDNyrpUAkAH90lKGdCCmziAv1h3edVc3kw37XamSr
34670 hRSGlVuXMlBvPci6Zgzj/L24ScF2iUkZ/cCovYmjZy/Gn7xxGWC4LeksyZB2ZnuU4q941mVTXTzW
34671 nLLPKQP5L6RQstRIzgUyVYr9smRMDuSYB3Xbf9+5CFVghTAp+XtIpGmG4zU/HoZdenoVve8AjhUi
34672 VBcAkCaTvA5JaJG/+EfTnZVCwQ5N328mz8MYIWJmQ3DW1cAH4QIDAQABo3QwcjARBglghkgBhvhC
34673 AQEEBAMCAAcwHwYDVR0jBBgwFoAUVeSB0RGAvtiJuQijMfmhJAkWuXAwHQYDVR0OBBYEFFXkgdER
34674 gL7YibkIozH5oSQJFrlwMB0GCSqGSIb2fQdBAAQQMA4bCFY1LjA6NC4wAwIEkDANBgkqhkiG9w0B
34675 AQUFAAOCAQEAWUesIYSKF8mciVMeuoCFGsY8Tj6xnLZ8xpJdGGQC49MGCBFhfGPjK50xA3B20qMo
34676 oPS7mmNz7W3lKtvtFKkrxjYR0CvrB4ul2p5cGZ1WEvVUKcgF7bISKo30Axv/55IQh7A6tcOdBTcS
34677 o8f0FbnVpDkWm1M6I5HxqIKiaohowXkCIryqptau37AUX7iH0N18f3v/rxzP5tsHrV7bhZ3QKw0z
34678 2wTR5klAEyt2+z7pnIkPFc4YsIV4IU9rTw76NmfNB/L/CNDi3tm/Kq+4h4YhPATKt5Rof8886ZjX
34679 OP/swNlQ8C5LWK5Gb9Auw2DaclVyvUxFnmG6v4SBkgPR0ml8xQ==
34680 -----END CERTIFICATE-----
34681
34682 Baltimore CyberTrust Root
34683 =========================
34684 -----BEGIN CERTIFICATE-----
34685 MIIDdzCCAl+gAwIBAgIEAgAAuTANBgkqhkiG9w0BAQUFADBaMQswCQYDVQQGEwJJRTESMBAGA1UE
34686 ChMJQmFsdGltb3JlMRMwEQYDVQQLEwpDeWJlclRydXN0MSIwIAYDVQQDExlCYWx0aW1vcmUgQ3li
34687 ZXJUcnVzdCBSb290MB4XDTAwMDUxMjE4NDYwMFoXDTI1MDUxMjIzNTkwMFowWjELMAkGA1UEBhMC
34688 SUUxEjAQBgNVBAoTCUJhbHRpbW9yZTETMBEGA1UECxMKQ3liZXJUcnVzdDEiMCAGA1UEAxMZQmFs
34689 dGltb3JlIEN5YmVyVHJ1c3QgUm9vdDCCASIwDQYJKoZIhvcNAQEBBQADggEPADCCAQoCggEBAKME
34690 uyKrmD1X6CZymrV51Cni4eiVgLGw41uOKymaZN+hXe2wCQVt2yguzmKiYv60iNoS6zjrIZ3AQSsB
34691 UnuId9Mcj8e6uYi1agnnc+gRQKfRzMpijS3ljwumUNKoUMMo6vWrJYeKmpYcqWe4PwzV9/lSEy/C
34692 G9VwcPCPwBLKBsua4dnKM3p31vjsufFoREJIE9LAwqSuXmD+tqYF/LTdB1kC1FkYmGP1pWPgkAx9
34693 XbIGevOF6uvUA65ehD5f/xXtabz5OTZydc93Uk3zyZAsuT3lySNTPx8kmCFcB5kpvcY67Oduhjpr
34694 l3RjM71oGDHweI12v/yejl0qhqdNkNwnGjkCAwEAAaNFMEMwHQYDVR0OBBYEFOWdWTCCR1jMrPoI
34695 VDaGezq1BE3wMBIGA1UdEwEB/wQIMAYBAf8CAQMwDgYDVR0PAQH/BAQDAgEGMA0GCSqGSIb3DQEB
34696 BQUAA4IBAQCFDF2O5G9RaEIFoN27TyclhAO992T9Ldcw46QQF+vaKSm2eT929hkTI7gQCvlYpNRh
34697 cL0EYWoSihfVCr3FvDB81ukMJY2GQE/szKN+OMY3EU/t3WgxjkzSswF07r51XgdIGn9w/xZchMB5
34698 hbgF/X++ZRGjD8ACtPhSNzkE1akxehi/oCr0Epn3o0WC4zxe9Z2etciefC7IpJ5OCBRLbf1wbWsa
34699 Y71k5h+3zvDyny67G7fyUIhzksLi4xaNmjICq44Y3ekQEe5+NauQrz4wlHrQMz2nZQ/1/I6eYs9H
34700 RCwBXbsdtTLSR9I4LtD+gdwyah617jzV/OeBHRnDJELqYzmp
34701 -----END CERTIFICATE-----
34702
34703 Equifax Secure Global eBusiness CA
34704 ==================================
34705 -----BEGIN CERTIFICATE-----
34706 MIICkDCCAfmgAwIBAgIBATANBgkqhkiG9w0BAQQFADBaMQswCQYDVQQGEwJVUzEcMBoGA1UEChMT
34707 RXF1aWZheCBTZWN1cmUgSW5jLjEtMCsGA1UEAxMkRXF1aWZheCBTZWN1cmUgR2xvYmFsIGVCdXNp
34708 bmVzcyBDQS0xMB4XDTk5MDYyMTA0MDAwMFoXDTIwMDYyMTA0MDAwMFowWjELMAkGA1UEBhMCVVMx
34709 HDAaBgNVBAoTE0VxdWlmYXggU2VjdXJlIEluYy4xLTArBgNVBAMTJEVxdWlmYXggU2VjdXJlIEds
34710 b2JhbCBlQnVzaW5lc3MgQ0EtMTCBnzANBgkqhkiG9w0BAQEFAAOBjQAwgYkCgYEAuucXkAJlsTRV
34711 PEnCUdXfp9E3j9HngXNBUmCbnaEXJnitx7HoJpQytd4zjTov2/KaelpzmKNc6fuKcxtc58O/gGzN
34712 qfTWK8D3+ZmqY6KxRwIP1ORROhI8bIpaVIRw28HFkM9yRcuoWcDNM50/o5brhTMhHD4ePmBudpxn
34713 hcXIw2ECAwEAAaNmMGQwEQYJYIZIAYb4QgEBBAQDAgAHMA8GA1UdEwEB/wQFMAMBAf8wHwYDVR0j
34714 BBgwFoAUvqigdHJQa0S3ySPY+6j/s1draGwwHQYDVR0OBBYEFL6ooHRyUGtEt8kj2Puo/7NXa2hs
34715 MA0GCSqGSIb3DQEBBAUAA4GBADDiAVGqx+pf2rnQZQ8w1j7aDRRJbpGTJxQx78T3LUX47Me/okEN
34716 I7SS+RkAZ70Br83gcfxaz2TE4JaY0KNA4gGK7ycH8WUBikQtBmV1UsCGECAhX2xrD2yuCRyv8qIY
34717 NMR1pHMc8Y3c7635s3a0kr/clRAevsvIO1qEYBlWlKlV
34718 -----END CERTIFICATE-----
34719
34720 Equifax Secure eBusiness CA 1
34721 =============================
34722 -----BEGIN CERTIFICATE-----
34723 MIICgjCCAeugAwIBAgIBBDANBgkqhkiG9w0BAQQFADBTMQswCQYDVQQGEwJVUzEcMBoGA1UEChMT
34724 RXF1aWZheCBTZWN1cmUgSW5jLjEmMCQGA1UEAxMdRXF1aWZheCBTZWN1cmUgZUJ1c2luZXNzIENB
34725 LTEwHhcNOTkwNjIxMDQwMDAwWhcNMjAwNjIxMDQwMDAwWjBTMQswCQYDVQQGEwJVUzEcMBoGA1UE
34726 ChMTRXF1aWZheCBTZWN1cmUgSW5jLjEmMCQGA1UEAxMdRXF1aWZheCBTZWN1cmUgZUJ1c2luZXNz
34727 IENBLTEwgZ8wDQYJKoZIhvcNAQEBBQADgY0AMIGJAoGBAM4vGbwXt3fek6lfWg0XTzQaDJj0ItlZ
34728 1MRoRvC0NcWFAyDGr0WlIVFFQesWWDYyb+JQYmT5/VGcqiTZ9J2DKocKIdMSODRsjQBuWqDZQu4a
34729 IZX5UkxVWsUPOE9G+m34LjXWHXzr4vCwdYDIqROsvojvOm6rXyo4YgKwEnv+j6YDAgMBAAGjZjBk
34730 MBEGCWCGSAGG+EIBAQQEAwIABzAPBgNVHRMBAf8EBTADAQH/MB8GA1UdIwQYMBaAFEp4MlIR21kW
34731 Nl7fwRQ2QGpHfEyhMB0GA1UdDgQWBBRKeDJSEdtZFjZe38EUNkBqR3xMoTANBgkqhkiG9w0BAQQF
34732 AAOBgQB1W6ibAxHm6VZMzfmpTMANmvPMZWnmJXbMWbfWVMMdzZmsGd20hdXgPfxiIKeES1hl8eL5
34733 lSE/9dR+WB5Hh1Q+WKG1tfgq73HnvMP2sUlG4tega+VWeponmHxGYhTnyfxuAxJ5gDgdSIKN/Bf+
34734 KpYrtWKmpj29f5JZzVoqgrI3eQ==
34735 -----END CERTIFICATE-----
34736
34737 Equifax Secure eBusiness CA 2
34738 =============================
34739 -----BEGIN CERTIFICATE-----
34740 MIIDIDCCAomgAwIBAgIEN3DPtTANBgkqhkiG9w0BAQUFADBOMQswCQYDVQQGEwJVUzEXMBUGA1UE
34741 ChMORXF1aWZheCBTZWN1cmUxJjAkBgNVBAsTHUVxdWlmYXggU2VjdXJlIGVCdXNpbmVzcyBDQS0y
34742 MB4XDTk5MDYyMzEyMTQ0NVoXDTE5MDYyMzEyMTQ0NVowTjELMAkGA1UEBhMCVVMxFzAVBgNVBAoT
34743 DkVxdWlmYXggU2VjdXJlMSYwJAYDVQQLEx1FcXVpZmF4IFNlY3VyZSBlQnVzaW5lc3MgQ0EtMjCB
34744 nzANBgkqhkiG9w0BAQEFAAOBjQAwgYkCgYEA5Dk5kx5SBhsoNviyoynF7Y6yEb3+6+e0dMKP/wXn
34745 2Z0GvxLIPw7y1tEkshHe0XMJitSxLJgJDR5QRrKDpkWNYmi7hRsgcDKqQM2mll/EcTc/BPO3QSQ5
34746 BxoeLmFYoBIL5aXfxavqN3HMHMg3OrmXUqesxWoklE6ce8/AatbfIb0CAwEAAaOCAQkwggEFMHAG
34747 A1UdHwRpMGcwZaBjoGGkXzBdMQswCQYDVQQGEwJVUzEXMBUGA1UEChMORXF1aWZheCBTZWN1cmUx
34748 JjAkBgNVBAsTHUVxdWlmYXggU2VjdXJlIGVCdXNpbmVzcyBDQS0yMQ0wCwYDVQQDEwRDUkwxMBoG
34749 A1UdEAQTMBGBDzIwMTkwNjIzMTIxNDQ1WjALBgNVHQ8EBAMCAQYwHwYDVR0jBBgwFoAUUJ4L6q9e
34750 uSBIplBqy/3YIHqngnYwHQYDVR0OBBYEFFCeC+qvXrkgSKZQasv92CB6p4J2MAwGA1UdEwQFMAMB
34751 Af8wGgYJKoZIhvZ9B0EABA0wCxsFVjMuMGMDAgbAMA0GCSqGSIb3DQEBBQUAA4GBAAyGgq3oThr1
34752 jokn4jVYPSm0B482UJW/bsGe68SQsoWou7dC4A8HOd/7npCy0cE+U58DRLB+S/Rv5Hwf5+Kx5Lia
34753 78O9zt4LMjTZ3ijtM2vE1Nc9ElirfQkty3D1E4qUoSek1nDFbZS1yX2doNLGCEnZZpum0/QL3MUm
34754 V+GRMOrN
34755 -----END CERTIFICATE-----
34756
34757 AddTrust Low-Value Services Root
34758 ================================
34759 -----BEGIN CERTIFICATE-----
34760 MIIEGDCCAwCgAwIBAgIBATANBgkqhkiG9w0BAQUFADBlMQswCQYDVQQGEwJTRTEUMBIGA1UEChML
34761 QWRkVHJ1c3QgQUIxHTAbBgNVBAsTFEFkZFRydXN0IFRUUCBOZXR3b3JrMSEwHwYDVQQDExhBZGRU
34762 cnVzdCBDbGFzcyAxIENBIFJvb3QwHhcNMDAwNTMwMTAzODMxWhcNMjAwNTMwMTAzODMxWjBlMQsw
34763 CQYDVQQGEwJTRTEUMBIGA1UEChMLQWRkVHJ1c3QgQUIxHTAbBgNVBAsTFEFkZFRydXN0IFRUUCBO
34764 ZXR3b3JrMSEwHwYDVQQDExhBZGRUcnVzdCBDbGFzcyAxIENBIFJvb3QwggEiMA0GCSqGSIb3DQEB
34765 AQUAA4IBDwAwggEKAoIBAQCWltQhSWDia+hBBwzexODcEyPNwTXH+9ZOEQpnXvUGW2ulCDtbKRY6
34766 54eyNAbFvAWlA3yCyykQruGIgb3WntP+LVbBFc7jJp0VLhD7Bo8wBN6ntGO0/7Gcrjyvd7ZWxbWr
34767 oulpOj0OM3kyP3CCkplhbY0wCI9xP6ZIVxn4JdxLZlyldI+Yrsj5wAYi56xz36Uu+1LcsRVlIPo1
34768 Zmne3yzxbrww2ywkEtvrNTVokMsAsJchPXQhI2U0K7t4WaPW4XY5mqRJjox0r26kmqPZm9I4XJui
34769 GMx1I4S+6+JNM3GOGvDC+Mcdoq0Dlyz4zyXG9rgkMbFjXZJ/Y/AlyVMuH79NAgMBAAGjgdIwgc8w
34770 HQYDVR0OBBYEFJWxtPCUtr3H2tERCSG+wa9J/RB7MAsGA1UdDwQEAwIBBjAPBgNVHRMBAf8EBTAD
34771 AQH/MIGPBgNVHSMEgYcwgYSAFJWxtPCUtr3H2tERCSG+wa9J/RB7oWmkZzBlMQswCQYDVQQGEwJT
34772 RTEUMBIGA1UEChMLQWRkVHJ1c3QgQUIxHTAbBgNVBAsTFEFkZFRydXN0IFRUUCBOZXR3b3JrMSEw
34773 HwYDVQQDExhBZGRUcnVzdCBDbGFzcyAxIENBIFJvb3SCAQEwDQYJKoZIhvcNAQEFBQADggEBACxt
34774 ZBsfzQ3duQH6lmM0MkhHma6X7f1yFqZzR1r0693p9db7RcwpiURdv0Y5PejuvE1Uhh4dbOMXJ0Ph
34775 iVYrqW9yTkkz43J8KiOavD7/KCrto/8cI7pDVwlnTUtiBi34/2ydYB7YHEt9tTEv2dB8Xfjea4MY
34776 eDdXL+gzB2ffHsdrKpV2ro9Xo/D0UrSpUwjP4E/TelOL/bscVjby/rK25Xa71SJlpz/+0WatC7xr
34777 mYbvP33zGDLKe8bjq2RGlfgmadlVg3sslgf/WSxEo8bl6ancoWOAWiFeIc9TVPC6b4nbqKqVz4vj
34778 ccweGyBECMB6tkD9xOQ14R0WHNC8K47Wcdk=
34779 -----END CERTIFICATE-----
34780
34781 AddTrust External Root
34782 ======================
34783 -----BEGIN CERTIFICATE-----
34784 MIIENjCCAx6gAwIBAgIBATANBgkqhkiG9w0BAQUFADBvMQswCQYDVQQGEwJTRTEUMBIGA1UEChML
34785 QWRkVHJ1c3QgQUIxJjAkBgNVBAsTHUFkZFRydXN0IEV4dGVybmFsIFRUUCBOZXR3b3JrMSIwIAYD
34786 VQQDExlBZGRUcnVzdCBFeHRlcm5hbCBDQSBSb290MB4XDTAwMDUzMDEwNDgzOFoXDTIwMDUzMDEw
34787 NDgzOFowbzELMAkGA1UEBhMCU0UxFDASBgNVBAoTC0FkZFRydXN0IEFCMSYwJAYDVQQLEx1BZGRU
34788 cnVzdCBFeHRlcm5hbCBUVFAgTmV0d29yazEiMCAGA1UEAxMZQWRkVHJ1c3QgRXh0ZXJuYWwgQ0Eg
34789 Um9vdDCCASIwDQYJKoZIhvcNAQEBBQADggEPADCCAQoCggEBALf3GjPm8gAELTngTlvtH7xsD821
34790 +iO2zt6bETOXpClMfZOfvUq8k+0DGuOPz+VtUFrWlymUWoCwSXrbLpX9uMq/NzgtHj6RQa1wVsfw
34791 Tz/oMp50ysiQVOnGXw94nZpAPA6sYapeFI+eh6FqUNzXmk6vBbOmcZSccbNQYArHE504B4YCqOmo
34792 aSYYkKtMsE8jqzpPhNjfzp/haW+710LXa0Tkx63ubUFfclpxCDezeWWkWaCUN/cALw3CknLa0Dhy
34793 2xSoRcRdKn23tNbE7qzNE0S3ySvdQwAl+mG5aWpYIxG3pzOPVnVZ9c0p10a3CitlttNCbxWyuHv7
34794 7+ldU9U0WicCAwEAAaOB3DCB2TAdBgNVHQ4EFgQUrb2YejS0Jvf6xCZU7wO94CTLVBowCwYDVR0P
34795 BAQDAgEGMA8GA1UdEwEB/wQFMAMBAf8wgZkGA1UdIwSBkTCBjoAUrb2YejS0Jvf6xCZU7wO94CTL
34796 VBqhc6RxMG8xCzAJBgNVBAYTAlNFMRQwEgYDVQQKEwtBZGRUcnVzdCBBQjEmMCQGA1UECxMdQWRk
34797 VHJ1c3QgRXh0ZXJuYWwgVFRQIE5ldHdvcmsxIjAgBgNVBAMTGUFkZFRydXN0IEV4dGVybmFsIENB
34798 IFJvb3SCAQEwDQYJKoZIhvcNAQEFBQADggEBALCb4IUlwtYj4g+WBpKdQZic2YR5gdkeWxQHIzZl
34799 j7DYd7usQWxHYINRsPkyPef89iYTx4AWpb9a/IfPeHmJIZriTAcKhjW88t5RxNKWt9x+Tu5w/Rw5
34800 6wwCURQtjr0W4MHfRnXnJK3s9EK0hZNwEGe6nQY1ShjTK3rMUUKhemPR5ruhxSvCNr4TDea9Y355
34801 e6cJDUCrat2PisP29owaQgVR1EX1n6diIWgVIEM8med8vSTYqZEXc4g/VhsxOBi0cQ+azcgOno4u
34802 G+GMmIPLHzHxREzGBHNJdmAPx/i9F4BrLunMTA5amnkPIAou1Z5jJh5VkpTYghdae9C8x49OhgQ=
34803 -----END CERTIFICATE-----
34804
34805 AddTrust Public Services Root
34806 =============================
34807 -----BEGIN CERTIFICATE-----
34808 MIIEFTCCAv2gAwIBAgIBATANBgkqhkiG9w0BAQUFADBkMQswCQYDVQQGEwJTRTEUMBIGA1UEChML
34809 QWRkVHJ1c3QgQUIxHTAbBgNVBAsTFEFkZFRydXN0IFRUUCBOZXR3b3JrMSAwHgYDVQQDExdBZGRU
34810 cnVzdCBQdWJsaWMgQ0EgUm9vdDAeFw0wMDA1MzAxMDQxNTBaFw0yMDA1MzAxMDQxNTBaMGQxCzAJ
34811 BgNVBAYTAlNFMRQwEgYDVQQKEwtBZGRUcnVzdCBBQjEdMBsGA1UECxMUQWRkVHJ1c3QgVFRQIE5l
34812 dHdvcmsxIDAeBgNVBAMTF0FkZFRydXN0IFB1YmxpYyBDQSBSb290MIIBIjANBgkqhkiG9w0BAQEF
34813 AAOCAQ8AMIIBCgKCAQEA6Rowj4OIFMEg2Dybjxt+A3S72mnTRqX4jsIMEZBRpS9mVEBV6tsfSlbu
34814 nyNu9DnLoblv8n75XYcmYZ4c+OLspoH4IcUkzBEMP9smcnrHAZcHF/nXGCwwfQ56HmIexkvA/X1i
34815 d9NEHif2P0tEs7c42TkfYNVRknMDtABp4/MUTu7R3AnPdzRGULD4EfL+OHn3Bzn+UZKXC1sIXzSG
34816 Aa2Il+tmzV7R/9x98oTaunet3IAIx6eH1lWfl2royBFkuucZKT8Rs3iQhCBSWxHveNCD9tVIkNAw
34817 HM+A+WD+eeSI8t0A65RF62WUaUC6wNW0uLp9BBGo6zEFlpROWCGOn9Bg/QIDAQABo4HRMIHOMB0G
34818 A1UdDgQWBBSBPjfYkrAfd59ctKtzquf2NGAv+jALBgNVHQ8EBAMCAQYwDwYDVR0TAQH/BAUwAwEB
34819 /zCBjgYDVR0jBIGGMIGDgBSBPjfYkrAfd59ctKtzquf2NGAv+qFopGYwZDELMAkGA1UEBhMCU0Ux
34820 FDASBgNVBAoTC0FkZFRydXN0IEFCMR0wGwYDVQQLExRBZGRUcnVzdCBUVFAgTmV0d29yazEgMB4G
34821 A1UEAxMXQWRkVHJ1c3QgUHVibGljIENBIFJvb3SCAQEwDQYJKoZIhvcNAQEFBQADggEBAAP3FUr4
34822 JNojVhaTdt02KLmuG7jD8WS6IBh4lSknVwW8fCr0uVFV2ocC3g8WFzH4qnkuCRO7r7IgGRLlk/lL
34823 +YPoRNWyQSW/iHVv/xD8SlTQX/D67zZzfRs2RcYhbbQVuE7PnFylPVoAjgbjPGsye/Kf8Lb93/Ao
34824 GEjwxrzQvzSAlsJKsW2Ox5BF3i9nrEUEo3rcVZLJR2bYGozH7ZxOmuASu7VqTITh4SINhwBk/ox9
34825 Yjllpu9CtoAlEmEBqCQTcAARJl/6NVDFSMwGR+gn2HCNX2TmoUQmXiLsks3/QppEIW1cxeMiHV9H
34826 EufOX1362KqxMy3ZdvJOOjMMK7MtkAY=
34827 -----END CERTIFICATE-----
34828
34829 AddTrust Qualified Certificates Root
34830 ====================================
34831 -----BEGIN CERTIFICATE-----
34832 MIIEHjCCAwagAwIBAgIBATANBgkqhkiG9w0BAQUFADBnMQswCQYDVQQGEwJTRTEUMBIGA1UEChML
34833 QWRkVHJ1c3QgQUIxHTAbBgNVBAsTFEFkZFRydXN0IFRUUCBOZXR3b3JrMSMwIQYDVQQDExpBZGRU
34834 cnVzdCBRdWFsaWZpZWQgQ0EgUm9vdDAeFw0wMDA1MzAxMDQ0NTBaFw0yMDA1MzAxMDQ0NTBaMGcx
34835 CzAJBgNVBAYTAlNFMRQwEgYDVQQKEwtBZGRUcnVzdCBBQjEdMBsGA1UECxMUQWRkVHJ1c3QgVFRQ
34836 IE5ldHdvcmsxIzAhBgNVBAMTGkFkZFRydXN0IFF1YWxpZmllZCBDQSBSb290MIIBIjANBgkqhkiG
34837 9w0BAQEFAAOCAQ8AMIIBCgKCAQEA5B6a/twJWoekn0e+EV+vhDTbYjx5eLfpMLXsDBwqxBb/4Oxx
34838 64r1EW7tTw2R0hIYLUkVAcKkIhPHEWT/IhKauY5cLwjPcWqzZwFZ8V1G87B4pfYOQnrjfxvM0PC3
34839 KP0q6p6zsLkEqv32x7SxuCqg+1jxGaBvcCV+PmlKfw8i2O+tCBGaKZnhqkRFmhJePp1tUvznoD1o
34840 L/BLcHwTOK28FSXx1s6rosAx1i+f4P8UWfyEk9mHfExUE+uf0S0R+Bg6Ot4l2ffTQO2kBhLEO+GR
34841 wVY18BTcZTYJbqukB8c10cIDMzZbdSZtQvESa0NvS3GU+jQd7RNuyoB/mC9suWXY6QIDAQABo4HU
34842 MIHRMB0GA1UdDgQWBBQ5lYtii1zJ1IC6WA+XPxUIQ8yYpzALBgNVHQ8EBAMCAQYwDwYDVR0TAQH/
34843 BAUwAwEB/zCBkQYDVR0jBIGJMIGGgBQ5lYtii1zJ1IC6WA+XPxUIQ8yYp6FrpGkwZzELMAkGA1UE
34844 BhMCU0UxFDASBgNVBAoTC0FkZFRydXN0IEFCMR0wGwYDVQQLExRBZGRUcnVzdCBUVFAgTmV0d29y
34845 azEjMCEGA1UEAxMaQWRkVHJ1c3QgUXVhbGlmaWVkIENBIFJvb3SCAQEwDQYJKoZIhvcNAQEFBQAD
34846 ggEBABmrder4i2VhlRO6aQTvhsoToMeqT2QbPxj2qC0sVY8FtzDqQmodwCVRLae/DLPt7wh/bDxG
34847 GuoYQ992zPlmhpwsaPXpF/gxsxjE1kh9I0xowX67ARRvxdlu3rsEQmr49lx95dr6h+sNNVJn0J6X
34848 dgWTP5XHAeZpVTh/EGGZyeNfpso+gmNIquIISD6q8rKFYqa0p9m9N5xotS1WfbC3P6CxB9bpT9ze
34849 RXEwMn8bLgn5v1Kh7sKAPgZcLlVAwRv1cEWw3F369nJad9Jjzc9YiQBCYz95OdBEsIJuQRno3eDB
34850 iFrRHnGTHyQwdOUeqN48Jzd/g66ed8/wMLH/S5noxqE=
34851 -----END CERTIFICATE-----
34852
34853 Entrust Root Certification Authority
34854 ====================================
34855 -----BEGIN CERTIFICATE-----
34856 MIIEkTCCA3mgAwIBAgIERWtQVDANBgkqhkiG9w0BAQUFADCBsDELMAkGA1UEBhMCVVMxFjAUBgNV
34857 BAoTDUVudHJ1c3QsIEluYy4xOTA3BgNVBAsTMHd3dy5lbnRydXN0Lm5ldC9DUFMgaXMgaW5jb3Jw
34858 b3JhdGVkIGJ5IHJlZmVyZW5jZTEfMB0GA1UECxMWKGMpIDIwMDYgRW50cnVzdCwgSW5jLjEtMCsG
34859 A1UEAxMkRW50cnVzdCBSb290IENlcnRpZmljYXRpb24gQXV0aG9yaXR5MB4XDTA2MTEyNzIwMjM0
34860 MloXDTI2MTEyNzIwNTM0MlowgbAxCzAJBgNVBAYTAlVTMRYwFAYDVQQKEw1FbnRydXN0LCBJbmMu
34861 MTkwNwYDVQQLEzB3d3cuZW50cnVzdC5uZXQvQ1BTIGlzIGluY29ycG9yYXRlZCBieSByZWZlcmVu
34862 Y2UxHzAdBgNVBAsTFihjKSAyMDA2IEVudHJ1c3QsIEluYy4xLTArBgNVBAMTJEVudHJ1c3QgUm9v
34863 dCBDZXJ0aWZpY2F0aW9uIEF1dGhvcml0eTCCASIwDQYJKoZIhvcNAQEBBQADggEPADCCAQoCggEB
34864 ALaVtkNC+sZtKm9I35RMOVcF7sN5EUFoNu3s/poBj6E4KPz3EEZmLk0eGrEaTsbRwJWIsMn/MYsz
34865 A9u3g3s+IIRe7bJWKKf44LlAcTfFy0cOlypowCKVYhXbR9n10Cv/gkvJrT7eTNuQgFA/CYqEAOww
34866 Cj0Yzfv9KlmaI5UXLEWeH25DeW0MXJj+SKfFI0dcXv1u5x609mhF0YaDW6KKjbHjKYD+JXGIrb68
34867 j6xSlkuqUY3kEzEZ6E5Nn9uss2rVvDlUccp6en+Q3X0dgNmBu1kmwhH+5pPi94DkZfs0Nw4pgHBN
34868 rziGLp5/V6+eF67rHMsoIV+2HNjnogQi+dPa2MsCAwEAAaOBsDCBrTAOBgNVHQ8BAf8EBAMCAQYw
34869 DwYDVR0TAQH/BAUwAwEB/zArBgNVHRAEJDAigA8yMDA2MTEyNzIwMjM0MlqBDzIwMjYxMTI3MjA1
34870 MzQyWjAfBgNVHSMEGDAWgBRokORnpKZTgMeGZqTx90tD+4S9bTAdBgNVHQ4EFgQUaJDkZ6SmU4DH
34871 hmak8fdLQ/uEvW0wHQYJKoZIhvZ9B0EABBAwDhsIVjcuMTo0LjADAgSQMA0GCSqGSIb3DQEBBQUA
34872 A4IBAQCT1DCw1wMgKtD5Y+iRDAUgqV8ZyntyTtSx29CW+1RaGSwMCPeyvIWonX9tO1KzKtvn1ISM
34873 Y/YPyyYBkVBs9F8U4pN0wBOeMDpQ47RgxRzwIkSNcUesyBrJ6ZuaAGAT/3B+XxFNSRuzFVJ7yVTa
34874 v52Vr2ua2J7p8eRDjeIRRDq/r72DQnNSi6q7pynP9WQcCk3RvKqsnyrQ/39/2n3qse0wJcGE2jTS
34875 W3iDVuycNsMm4hH2Z0kdkquM++v/eu6FSqdQgPCnXEqULl8FmTxSQeDNtGPPAUO6nIPcj2A781q0
34876 tHuu2guQOHXvgR1m0vdXcDazv/wor3ElhVsT/h5/WrQ8
34877 -----END CERTIFICATE-----
34878
34879 RSA Security 2048 v3
34880 ====================
34881 -----BEGIN CERTIFICATE-----
34882 MIIDYTCCAkmgAwIBAgIQCgEBAQAAAnwAAAAKAAAAAjANBgkqhkiG9w0BAQUFADA6MRkwFwYDVQQK
34883 ExBSU0EgU2VjdXJpdHkgSW5jMR0wGwYDVQQLExRSU0EgU2VjdXJpdHkgMjA0OCBWMzAeFw0wMTAy
34884 MjIyMDM5MjNaFw0yNjAyMjIyMDM5MjNaMDoxGTAXBgNVBAoTEFJTQSBTZWN1cml0eSBJbmMxHTAb
34885 BgNVBAsTFFJTQSBTZWN1cml0eSAyMDQ4IFYzMIIBIjANBgkqhkiG9w0BAQEFAAOCAQ8AMIIBCgKC
34886 AQEAt49VcdKA3XtpeafwGFAyPGJn9gqVB93mG/Oe2dJBVGutn3y+Gc37RqtBaB4Y6lXIL5F4iSj7
34887 Jylg/9+PjDvJSZu1pJTOAeo+tWN7fyb9Gd3AIb2E0S1PRsNO3Ng3OTsor8udGuorryGlwSMiuLgb
34888 WhOHV4PR8CDn6E8jQrAApX2J6elhc5SYcSa8LWrg903w8bYqODGBDSnhAMFRD0xS+ARaqn1y07iH
34889 KrtjEAMqs6FPDVpeRrc9DvV07Jmf+T0kgYim3WBU6JU2PcYJk5qjEoAAVZkZR73QpXzDuvsf9/UP
34890 +Ky5tfQ3mBMY3oVbtwyCO4dvlTlYMNpuAWgXIszACwIDAQABo2MwYTAPBgNVHRMBAf8EBTADAQH/
34891 MA4GA1UdDwEB/wQEAwIBBjAfBgNVHSMEGDAWgBQHw1EwpKrpRa41JPr/JCwz0LGdjDAdBgNVHQ4E
34892 FgQUB8NRMKSq6UWuNST6/yQsM9CxnYwwDQYJKoZIhvcNAQEFBQADggEBAF8+hnZuuDU8TjYcHnmY
34893 v/3VEhF5Ug7uMYm83X/50cYVIeiKAVQNOvtUudZj1LGqlk2iQk3UUx+LEN5/Zb5gEydxiKRz44Rj
34894 0aRV4VCT5hsOedBnvEbIvz8XDZXmxpBp3ue0L96VfdASPz0+f00/FGj1EVDVwfSQpQgdMWD/YIwj
34895 VAqv/qFuxdF6Kmh4zx6CCiC0H63lhbJqaHVOrSU3lIW+vaHU6rcMSzyd6BIA8F+sDeGscGNz9395
34896 nzIlQnQFgCi/vcEkllgVsRch6YlL2weIZ/QVrXA+L02FO8K32/6YaCOJ4XQP3vTFhGMpG8zLB8kA
34897 pKnXwiJPZ9d37CAFYd4=
34898 -----END CERTIFICATE-----
34899
34900 GeoTrust Global CA
34901 ==================
34902 -----BEGIN CERTIFICATE-----
34903 MIIDVDCCAjygAwIBAgIDAjRWMA0GCSqGSIb3DQEBBQUAMEIxCzAJBgNVBAYTAlVTMRYwFAYDVQQK
34904 Ew1HZW9UcnVzdCBJbmMuMRswGQYDVQQDExJHZW9UcnVzdCBHbG9iYWwgQ0EwHhcNMDIwNTIxMDQw
34905 MDAwWhcNMjIwNTIxMDQwMDAwWjBCMQswCQYDVQQGEwJVUzEWMBQGA1UEChMNR2VvVHJ1c3QgSW5j
34906 LjEbMBkGA1UEAxMSR2VvVHJ1c3QgR2xvYmFsIENBMIIBIjANBgkqhkiG9w0BAQEFAAOCAQ8AMIIB
34907 CgKCAQEA2swYYzD99BcjGlZ+W988bDjkcbd4kdS8odhM+KhDtgPpTSEHCIjaWC9mOSm9BXiLnTjo
34908 BbdqfnGk5sRgprDvgOSJKA+eJdbtg/OtppHHmMlCGDUUna2YRpIuT8rxh0PBFpVXLVDviS2Aelet
34909 8u5fa9IAjbkU+BQVNdnARqN7csiRv8lVK83Qlz6cJmTM386DGXHKTubU1XupGc1V3sjs0l44U+Vc
34910 T4wt/lAjNvxm5suOpDkZALeVAjmRCw7+OC7RHQWa9k0+bw8HHa8sHo9gOeL6NlMTOdReJivbPagU
34911 vTLrGAMoUgRx5aszPeE4uwc2hGKceeoWMPRfwCvocWvk+QIDAQABo1MwUTAPBgNVHRMBAf8EBTAD
34912 AQH/MB0GA1UdDgQWBBTAephojYn7qwVkDBF9qn1luMrMTjAfBgNVHSMEGDAWgBTAephojYn7qwVk
34913 DBF9qn1luMrMTjANBgkqhkiG9w0BAQUFAAOCAQEANeMpauUvXVSOKVCUn5kaFOSPeCpilKInZ57Q
34914 zxpeR+nBsqTP3UEaBU6bS+5Kb1VSsyShNwrrZHYqLizz/Tt1kL/6cdjHPTfStQWVYrmm3ok9Nns4
34915 d0iXrKYgjy6myQzCsplFAMfOEVEiIuCl6rYVSAlk6l5PdPcFPseKUgzbFbS9bZvlxrFUaKnjaZC2
34916 mqUPuLk/IH2uSrW4nOQdtqvmlKXBx4Ot2/Unhw4EbNX/3aBd7YdStysVAq45pmp06drE57xNNB6p
34917 XE0zX5IJL4hmXXeXxx12E6nV5fEWCRE11azbJHFwLJhWC9kXtNHjUStedejV0NxPNO3CBWaAocvm
34918 Mw==
34919 -----END CERTIFICATE-----
34920
34921 GeoTrust Global CA 2
34922 ====================
34923 -----BEGIN CERTIFICATE-----
34924 MIIDZjCCAk6gAwIBAgIBATANBgkqhkiG9w0BAQUFADBEMQswCQYDVQQGEwJVUzEWMBQGA1UEChMN
34925 R2VvVHJ1c3QgSW5jLjEdMBsGA1UEAxMUR2VvVHJ1c3QgR2xvYmFsIENBIDIwHhcNMDQwMzA0MDUw
34926 MDAwWhcNMTkwMzA0MDUwMDAwWjBEMQswCQYDVQQGEwJVUzEWMBQGA1UEChMNR2VvVHJ1c3QgSW5j
34927 LjEdMBsGA1UEAxMUR2VvVHJ1c3QgR2xvYmFsIENBIDIwggEiMA0GCSqGSIb3DQEBAQUAA4IBDwAw
34928 ggEKAoIBAQDvPE1APRDfO1MA4Wf+lGAVPoWI8YkNkMgoI5kF6CsgncbzYEbYwbLVjDHZ3CB5JIG/
34929 NTL8Y2nbsSpr7iFY8gjpeMtvy/wWUsiRxP89c96xPqfCfWbB9X5SJBri1WeR0IIQ13hLTytCOb1k
34930 LUCgsBDTOEhGiKEMuzozKmKY+wCdE1l/bztyqu6mD4b5BWHqZ38MN5aL5mkWRxHCJ1kDs6ZgwiFA
34931 Vvqgx306E+PsV8ez1q6diYD3Aecs9pYrEw15LNnA5IZ7S4wMcoKK+xfNAGw6EzywhIdLFnopsk/b
34932 HdQL82Y3vdj2V7teJHq4PIu5+pIaGoSe2HSPqht/XvT+RSIhAgMBAAGjYzBhMA8GA1UdEwEB/wQF
34933 MAMBAf8wHQYDVR0OBBYEFHE4NvICMVNHK266ZUapEBVYIAUJMB8GA1UdIwQYMBaAFHE4NvICMVNH
34934 K266ZUapEBVYIAUJMA4GA1UdDwEB/wQEAwIBhjANBgkqhkiG9w0BAQUFAAOCAQEAA/e1K6tdEPx7
34935 srJerJsOflN4WT5CBP51o62sgU7XAotexC3IUnbHLB/8gTKY0UvGkpMzNTEv/NgdRN3ggX+d6Yvh
34936 ZJFiCzkIjKx0nVnZellSlxG5FntvRdOW2TF9AjYPnDtuzywNA0ZF66D0f0hExghAzN4bcLUprbqL
34937 OzRldRtxIR0sFAqwlpW41uryZfspuk/qkZN0abby/+Ea0AzRdoXLiiW9l14sbxWZJue2Kf8i7MkC
34938 x1YAzUm5s2x7UwQa4qjJqhIFI8LO57sEAszAR6LkxCkvW0VXiVHuPOtSCP8HNR6fNWpHSlaY0VqF
34939 H4z1Ir+rzoPz4iIprn2DQKi6bA==
34940 -----END CERTIFICATE-----
34941
34942 GeoTrust Universal CA
34943 =====================
34944 -----BEGIN CERTIFICATE-----
34945 MIIFaDCCA1CgAwIBAgIBATANBgkqhkiG9w0BAQUFADBFMQswCQYDVQQGEwJVUzEWMBQGA1UEChMN
34946 R2VvVHJ1c3QgSW5jLjEeMBwGA1UEAxMVR2VvVHJ1c3QgVW5pdmVyc2FsIENBMB4XDTA0MDMwNDA1
34947 MDAwMFoXDTI5MDMwNDA1MDAwMFowRTELMAkGA1UEBhMCVVMxFjAUBgNVBAoTDUdlb1RydXN0IElu
34948 Yy4xHjAcBgNVBAMTFUdlb1RydXN0IFVuaXZlcnNhbCBDQTCCAiIwDQYJKoZIhvcNAQEBBQADggIP
34949 ADCCAgoCggIBAKYVVaCjxuAfjJ0hUNfBvitbtaSeodlyWL0AG0y/YckUHUWCq8YdgNY96xCcOq9t
34950 JPi8cQGeBvV8Xx7BDlXKg5pZMK4ZyzBIle0iN430SppyZj6tlcDgFgDgEB8rMQ7XlFTTQjOgNB0e
34951 RXbdT8oYN+yFFXoZCPzVx5zw8qkuEKmS5j1YPakWaDwvdSEYfyh3peFhF7em6fgemdtzbvQKoiFs
34952 7tqqhZJmr/Z6a4LauiIINQ/PQvE1+mrufislzDoR5G2vc7J2Ha3QsnhnGqQ5HFELZ1aD/ThdDc7d
34953 8Lsrlh/eezJS/R27tQahsiFepdaVaH/wmZ7cRQg+59IJDTWU3YBOU5fXtQlEIGQWFwMCTFMNaN7V
34954 qnJNk22CDtucvc+081xdVHppCZbW2xHBjXWotM85yM48vCR85mLK4b19p71XZQvk/iXttmkQ3Cga
34955 Rr0BHdCXteGYO8A3ZNY9lO4L4fUorgtWv3GLIylBjobFS1J72HGrH4oVpjuDWtdYAVHGTEHZf9hB
34956 Z3KiKN9gg6meyHv8U3NyWfWTehd2Ds735VzZC1U0oqpbtWpU5xPKV+yXbfReBi9Fi1jUIxaS5BZu
34957 KGNZMN9QAZxjiRqf2xeUgnA3wySemkfWWspOqGmJch+RbNt+nhutxx9z3SxPGWX9f5NAEC7S8O08
34958 ni4oPmkmM8V7AgMBAAGjYzBhMA8GA1UdEwEB/wQFMAMBAf8wHQYDVR0OBBYEFNq7LqqwDLiIJlF0
34959 XG0D08DYj3rWMB8GA1UdIwQYMBaAFNq7LqqwDLiIJlF0XG0D08DYj3rWMA4GA1UdDwEB/wQEAwIB
34960 hjANBgkqhkiG9w0BAQUFAAOCAgEAMXjmx7XfuJRAyXHEqDXsRh3ChfMoWIawC/yOsjmPRFWrZIRc
34961 aanQmjg8+uUfNeVE44B5lGiku8SfPeE0zTBGi1QrlaXv9z+ZhP015s8xxtxqv6fXIwjhmF7DWgh2
34962 qaavdy+3YL1ERmrvl/9zlcGO6JP7/TG37FcREUWbMPEaiDnBTzynANXH/KttgCJwpQzgXQQpAvvL
34963 oJHRfNbDflDVnVi+QTjruXU8FdmbyUqDWcDaU/0zuzYYm4UPFd3uLax2k7nZAY1IEKj79TiG8dsK
34964 xr2EoyNB3tZ3b4XUhRxQ4K5RirqNPnbiucon8l+f725ZDQbYKxek0nxru18UGkiPGkzns0ccjkxF
34965 KyDuSN/n3QmOGKjaQI2SJhFTYXNd673nxE0pN2HrrDktZy4W1vUAg4WhzH92xH3kt0tm7wNFYGm2
34966 DFKWkoRepqO1pD4r2czYG0eq8kTaT/kD6PAUyz/zg97QwVTjt+gKN02LIFkDMBmhLMi9ER/frslK
34967 xfMnZmaGrGiR/9nmUxwPi1xpZQomyB40w11Re9epnAahNt3ViZS82eQtDF4JbAiXfKM9fJP/P6EU
34968 p8+1Xevb2xzEdt+Iub1FBZUbrvxGakyvSOPOrg/SfuvmbJxPgWp6ZKy7PtXny3YuxadIwVyQD8vI
34969 P/rmMuGNG2+k5o7Y+SlIis5z/iw=
34970 -----END CERTIFICATE-----
34971
34972 GeoTrust Universal CA 2
34973 =======================
34974 -----BEGIN CERTIFICATE-----
34975 MIIFbDCCA1SgAwIBAgIBATANBgkqhkiG9w0BAQUFADBHMQswCQYDVQQGEwJVUzEWMBQGA1UEChMN
34976 R2VvVHJ1c3QgSW5jLjEgMB4GA1UEAxMXR2VvVHJ1c3QgVW5pdmVyc2FsIENBIDIwHhcNMDQwMzA0
34977 MDUwMDAwWhcNMjkwMzA0MDUwMDAwWjBHMQswCQYDVQQGEwJVUzEWMBQGA1UEChMNR2VvVHJ1c3Qg
34978 SW5jLjEgMB4GA1UEAxMXR2VvVHJ1c3QgVW5pdmVyc2FsIENBIDIwggIiMA0GCSqGSIb3DQEBAQUA
34979 A4ICDwAwggIKAoICAQCzVFLByT7y2dyxUxpZKeexw0Uo5dfR7cXFS6GqdHtXr0om/Nj1XqduGdt0
34980 DE81WzILAePb63p3NeqqWuDW6KFXlPCQo3RWlEQwAx5cTiuFJnSCegx2oG9NzkEtoBUGFF+3Qs17
34981 j1hhNNwqCPkuwwGmIkQcTAeC5lvO0Ep8BNMZcyfwqph/Lq9O64ceJHdqXbboW0W63MOhBW9Wjo8Q
34982 JqVJwy7XQYci4E+GymC16qFjwAGXEHm9ADwSbSsVsaxLse4YuU6W3Nx2/zu+z18DwPw76L5GG//a
34983 QMJS9/7jOvdqdzXQ2o3rXhhqMcceujwbKNZrVMaqW9eiLBsZzKIC9ptZvTdrhrVtgrrY6slWvKk2
34984 WP0+GfPtDCapkzj4T8FdIgbQl+rhrcZV4IErKIM6+vR7IVEAvlI4zs1meaj0gVbi0IMJR1FbUGrP
34985 20gaXT73y/Zl92zxlfgCOzJWgjl6W70viRu/obTo/3+NjN8D8WBOWBFM66M/ECuDmgFz2ZRthAAn
34986 ZqzwcEAJQpKtT5MNYQlRJNiS1QuUYbKHsu3/mjX/hVTK7URDrBs8FmtISgocQIgfksILAAX/8sgC
34987 SqSqqcyZlpwvWOB94b67B9xfBHJcMTTD7F8t4D1kkCLm0ey4Lt1ZrtmhN79UNdxzMk+MBB4zsslG
34988 8dhcyFVQyWi9qLo2CQIDAQABo2MwYTAPBgNVHRMBAf8EBTADAQH/MB0GA1UdDgQWBBR281Xh+qQ2
34989 +/CfXGJx7Tz0RzgQKzAfBgNVHSMEGDAWgBR281Xh+qQ2+/CfXGJx7Tz0RzgQKzAOBgNVHQ8BAf8E
34990 BAMCAYYwDQYJKoZIhvcNAQEFBQADggIBAGbBxiPz2eAubl/oz66wsCVNK/g7WJtAJDday6sWSf+z
34991 dXkzoS9tcBc0kf5nfo/sm+VegqlVHy/c1FEHEv6sFj4sNcZj/NwQ6w2jqtB8zNHQL1EuxBRa3ugZ
34992 4T7GzKQp5y6EqgYweHZUcyiYWTjgAA1i00J9IZ+uPTqM1fp3DRgrFg5fNuH8KrUwJM/gYwx7WBr+
34993 mbpCErGR9Hxo4sjoryzqyX6uuyo9DRXcNJW2GHSoag/HtPQTxORb7QrSpJdMKu0vbBKJPfEncKpq
34994 A1Ihn0CoZ1Dy81of398j9tx4TuaYT1U6U+Pv8vSfx3zYWK8pIpe44L2RLrB27FcRz+8pRPPphXpg
34995 Y+RdM4kX2TGq2tbzGDVyz4crL2MjhF2EjD9XoIj8mZEoJmmZ1I+XRL6O1UixpCgp8RW04eWe3fiP
34996 pm8m1wk8OhwRDqZsN/etRIcsKMfYdIKz0G9KV7s1KSegi+ghp4dkNl3M2Basx7InQJJVOCiNUW7d
34997 FGdTbHFcJoRNdVq2fmBWqU2t+5sel/MN2dKXVHfaPRK34B7vCAas+YWH6aLcr34YEoP9VhdBLtUp
34998 gn2Z9DH2canPLAEnpQW5qrJITirvn5NSUZU8UnOOVkwXQMAJKOSLakhT2+zNVVXxxvjpoixMptEm
34999 X36vWkzaH6byHCx+rgIW0lbQL1dTR+iS
35000 -----END CERTIFICATE-----
35001
35002 UTN-USER First-Network Applications
35003 ===================================
35004 -----BEGIN CERTIFICATE-----
35005 MIIEZDCCA0ygAwIBAgIQRL4Mi1AAJLQR0zYwS8AzdzANBgkqhkiG9w0BAQUFADCBozELMAkGA1UE
35006 BhMCVVMxCzAJBgNVBAgTAlVUMRcwFQYDVQQHEw5TYWx0IExha2UgQ2l0eTEeMBwGA1UEChMVVGhl
35007 IFVTRVJUUlVTVCBOZXR3b3JrMSEwHwYDVQQLExhodHRwOi8vd3d3LnVzZXJ0cnVzdC5jb20xKzAp
35008 BgNVBAMTIlVUTi1VU0VSRmlyc3QtTmV0d29yayBBcHBsaWNhdGlvbnMwHhcNOTkwNzA5MTg0ODM5
35009 WhcNMTkwNzA5MTg1NzQ5WjCBozELMAkGA1UEBhMCVVMxCzAJBgNVBAgTAlVUMRcwFQYDVQQHEw5T
35010 YWx0IExha2UgQ2l0eTEeMBwGA1UEChMVVGhlIFVTRVJUUlVTVCBOZXR3b3JrMSEwHwYDVQQLExho
35011 dHRwOi8vd3d3LnVzZXJ0cnVzdC5jb20xKzApBgNVBAMTIlVUTi1VU0VSRmlyc3QtTmV0d29yayBB
35012 cHBsaWNhdGlvbnMwggEiMA0GCSqGSIb3DQEBAQUAA4IBDwAwggEKAoIBAQCz+5Gh5DZVhawGNFug
35013 mliy+LUPBXeDrjKxdpJo7CNKyXY/45y2N3kDuatpjQclthln5LAbGHNhSuh+zdMvZOOmfAz6F4Cj
35014 DUeJT1FxL+78P/m4FoCHiZMlIJpDgmkkdihZNaEdwH+DBmQWICzTSaSFtMBhf1EI+GgVkYDLpdXu
35015 Ozr0hAReYFmnjDRy7rh4xdE7EkpvfmUnuaRVxblvQ6TFHSyZwFKkeEwVs0CYCGtDxgGwenv1axwi
35016 P8vv/6jQOkt2FZ7S0cYu49tXGzKiuG/ohqY/cKvlcJKrRB5AUPuco2LkbG6gyN7igEL66S/ozjIE
35017 j3yNtxyjNTwV3Z7DrpelAgMBAAGjgZEwgY4wCwYDVR0PBAQDAgHGMA8GA1UdEwEB/wQFMAMBAf8w
35018 HQYDVR0OBBYEFPqGydvguul49Uuo1hXf8NPhahQ8ME8GA1UdHwRIMEYwRKBCoECGPmh0dHA6Ly9j
35019 cmwudXNlcnRydXN0LmNvbS9VVE4tVVNFUkZpcnN0LU5ldHdvcmtBcHBsaWNhdGlvbnMuY3JsMA0G
35020 CSqGSIb3DQEBBQUAA4IBAQCk8yXM0dSRgyLQzDKrm5ZONJFUICU0YV8qAhXhi6r/fWRRzwr/vH3Y
35021 IWp4yy9Rb/hCHTO967V7lMPDqaAt39EpHx3+jz+7qEUqf9FuVSTiuwL7MT++6LzsQCv4AdRWOOTK
35022 RIK1YSAhZ2X28AvnNPilwpyjXEAfhZOVBt5P1CeptqX8Fs1zMT+4ZSfP1FMa8Kxun08FDAOBp4Qp
35023 xFq9ZFdyrTvPNximmMatBrTcCKME1SmklpoSZ0qMYEWd8SOasACcaLWYUNPvji6SZbFIPiG+FTAq
35024 DbUMo2s/rn9X9R+WfN9v3YIwLGUbQErNaLly7HF27FSOH4UMAWr6pjisH8SE
35025 -----END CERTIFICATE-----
35026
35027 America Online Root Certification Authority 1
35028 =============================================
35029 -----BEGIN CERTIFICATE-----
35030 MIIDpDCCAoygAwIBAgIBATANBgkqhkiG9w0BAQUFADBjMQswCQYDVQQGEwJVUzEcMBoGA1UEChMT
35031 QW1lcmljYSBPbmxpbmUgSW5jLjE2MDQGA1UEAxMtQW1lcmljYSBPbmxpbmUgUm9vdCBDZXJ0aWZp
35032 Y2F0aW9uIEF1dGhvcml0eSAxMB4XDTAyMDUyODA2MDAwMFoXDTM3MTExOTIwNDMwMFowYzELMAkG
35033 A1UEBhMCVVMxHDAaBgNVBAoTE0FtZXJpY2EgT25saW5lIEluYy4xNjA0BgNVBAMTLUFtZXJpY2Eg
35034 T25saW5lIFJvb3QgQ2VydGlmaWNhdGlvbiBBdXRob3JpdHkgMTCCASIwDQYJKoZIhvcNAQEBBQAD
35035 ggEPADCCAQoCggEBAKgv6KRpBgNHw+kqmP8ZonCaxlCyfqXfaE0bfA+2l2h9LaaLl+lkhsmj76CG
35036 v2BlnEtUiMJIxUo5vxTjWVXlGbR0yLQFOVwWpeKVBeASrlmLojNoWBym1BW32J/X3HGrfpq/m44z
35037 DyL9Hy7nBzbvYjnF3cu6JRQj3gzGPTzOggjmZj7aUTsWOqMFf6Dch9Wc/HKpoH145LcxVR5lu9Rh
35038 sCFg7RAycsWSJR74kEoYeEfffjA3PlAb2xzTa5qGUwew76wGePiEmf4hjUyAtgyC9mZweRrTT6PP
35039 8c9GsEsPPt2IYriMqQkoO3rHl+Ee5fSfwMCuJKDIodkP1nsmgmkyPacCAwEAAaNjMGEwDwYDVR0T
35040 AQH/BAUwAwEB/zAdBgNVHQ4EFgQUAK3Zo/Z59m50qX8zPYEX10zPM94wHwYDVR0jBBgwFoAUAK3Z
35041 o/Z59m50qX8zPYEX10zPM94wDgYDVR0PAQH/BAQDAgGGMA0GCSqGSIb3DQEBBQUAA4IBAQB8itEf
35042 GDeC4Liwo+1WlchiYZwFos3CYiZhzRAW18y0ZTTQEYqtqKkFZu90821fnZmv9ov761KyBZiibyrF
35043 VL0lvV+uyIbqRizBs73B6UlwGBaXCBOMIOAbLjpHyx7kADCVW/RFo8AasAFOq73AI25jP4BKxQft
35044 3OJvx8Fi8eNy1gTIdGcL+oiroQHIb/AUr9KZzVGTfu0uOMe9zkZQPXLjeSWdm4grECDdpbgyn43g
35045 Kd8hdIaC2y+CMMbHNYaz+ZZfRtsMRf3zUMNvxsNIrUam4SdHCh0Om7bCd39j8uB9Gr784N/Xx6ds
35046 sPmuujz9dLQR6FgNgLzTqIA6me11zEZ7
35047 -----END CERTIFICATE-----
35048
35049 America Online Root Certification Authority 2
35050 =============================================
35051 -----BEGIN CERTIFICATE-----
35052 MIIFpDCCA4ygAwIBAgIBATANBgkqhkiG9w0BAQUFADBjMQswCQYDVQQGEwJVUzEcMBoGA1UEChMT
35053 QW1lcmljYSBPbmxpbmUgSW5jLjE2MDQGA1UEAxMtQW1lcmljYSBPbmxpbmUgUm9vdCBDZXJ0aWZp
35054 Y2F0aW9uIEF1dGhvcml0eSAyMB4XDTAyMDUyODA2MDAwMFoXDTM3MDkyOTE0MDgwMFowYzELMAkG
35055 A1UEBhMCVVMxHDAaBgNVBAoTE0FtZXJpY2EgT25saW5lIEluYy4xNjA0BgNVBAMTLUFtZXJpY2Eg
35056 T25saW5lIFJvb3QgQ2VydGlmaWNhdGlvbiBBdXRob3JpdHkgMjCCAiIwDQYJKoZIhvcNAQEBBQAD
35057 ggIPADCCAgoCggIBAMxBRR3pPU0Q9oyxQcngXssNt79Hc9PwVU3dxgz6sWYFas14tNwC206B89en
35058 fHG8dWOgXeMHDEjsJcQDIPT/DjsS/5uN4cbVG7RtIuOx238hZK+GvFciKtZHgVdEglZTvYYUAQv8
35059 f3SkWq7xuhG1m1hagLQ3eAkzfDJHA1zEpYNI9FdWboE2JxhP7JsowtS013wMPgwr38oE18aO6lhO
35060 qKSlGBxsRZijQdEt0sdtjRnxrXm3gT+9BoInLRBYBbV4Bbkv2wxrkJB+FFk4u5QkE+XRnRTf04JN
35061 RvCAOVIyD+OEsnpD8l7eXz8d3eOyG6ChKiMDbi4BFYdcpnV1x5dhvt6G3NRI270qv0pV2uh9UPu0
35062 gBe4lL8BPeraunzgWGcXuVjgiIZGZ2ydEEdYMtA1fHkqkKJaEBEjNa0vzORKW6fIJ/KD3l67Xnfn
35063 6KVuY8INXWHQjNJsWiEOyiijzirplcdIz5ZvHZIlyMbGwcEMBawmxNJ10uEqZ8A9W6Wa6897Gqid
35064 FEXlD6CaZd4vKL3Ob5Rmg0gp2OpljK+T2WSfVVcmv2/LNzGZo2C7HK2JNDJiuEMhBnIMoVxtRsX6
35065 Kc8w3onccVvdtjc+31D1uAclJuW8tf48ArO3+L5DwYcRlJ4jbBeKuIonDFRH8KmzwICMoCfrHRnj
35066 B453cMor9H124HhnAgMBAAGjYzBhMA8GA1UdEwEB/wQFMAMBAf8wHQYDVR0OBBYEFE1FwWg4u3Op
35067 aaEg5+31IqEjFNeeMB8GA1UdIwQYMBaAFE1FwWg4u3OpaaEg5+31IqEjFNeeMA4GA1UdDwEB/wQE
35068 AwIBhjANBgkqhkiG9w0BAQUFAAOCAgEAZ2sGuV9FOypLM7PmG2tZTiLMubekJcmnxPBUlgtk87FY
35069 T15R/LKXeydlwuXK5w0MJXti4/qftIe3RUavg6WXSIylvfEWK5t2LHo1YGwRgJfMqZJS5ivmae2p
35070 +DYtLHe/YUjRYwu5W1LtGLBDQiKmsXeu3mnFzcccobGlHBD7GL4acN3Bkku+KVqdPzW+5X1R+FXg
35071 JXUjhx5c3LqdsKyzadsXg8n33gy8CNyRnqjQ1xU3c6U1uPx+xURABsPr+CKAXEfOAuMRn0T//Zoy
35072 zH1kUQ7rVyZ2OuMeIjzCpjbdGe+n/BLzJsBZMYVMnNjP36TMzCmT/5RtdlwTCJfy7aULTd3oyWgO
35073 ZtMADjMSW7yV5TKQqLPGbIOtd+6Lfn6xqavT4fG2wLHqiMDn05DpKJKUe2h7lyoKZy2FAjgQ5ANh
35074 1NolNscIWC2hp1GvMApJ9aZphwctREZ2jirlmjvXGKL8nDgQzMY70rUXOm/9riW99XJZZLF0Kjhf
35075 GEzfz3EEWjbUvy+ZnOjZurGV5gJLIaFb1cFPj65pbVPbAZO1XB4Y3WRayhgoPmMEEf0cjQAPuDff
35076 Z4qdZqkCapH/E8ovXYO8h5Ns3CRRFgQlZvqz2cK6Kb6aSDiCmfS/O0oxGfm/jiEzFMpPVF/7zvuP
35077 cX/9XhmgD0uRuMRUvAawRY8mkaKO/qk=
35078 -----END CERTIFICATE-----
35079
35080 Visa eCommerce Root
35081 ===================
35082 -----BEGIN CERTIFICATE-----
35083 MIIDojCCAoqgAwIBAgIQE4Y1TR0/BvLB+WUF1ZAcYjANBgkqhkiG9w0BAQUFADBrMQswCQYDVQQG
35084 EwJVUzENMAsGA1UEChMEVklTQTEvMC0GA1UECxMmVmlzYSBJbnRlcm5hdGlvbmFsIFNlcnZpY2Ug
35085 QXNzb2NpYXRpb24xHDAaBgNVBAMTE1Zpc2EgZUNvbW1lcmNlIFJvb3QwHhcNMDIwNjI2MDIxODM2
35086 WhcNMjIwNjI0MDAxNjEyWjBrMQswCQYDVQQGEwJVUzENMAsGA1UEChMEVklTQTEvMC0GA1UECxMm
35087 VmlzYSBJbnRlcm5hdGlvbmFsIFNlcnZpY2UgQXNzb2NpYXRpb24xHDAaBgNVBAMTE1Zpc2EgZUNv
35088 bW1lcmNlIFJvb3QwggEiMA0GCSqGSIb3DQEBAQUAA4IBDwAwggEKAoIBAQCvV95WHm6h2mCxlCfL
35089 F9sHP4CFT8icttD0b0/Pmdjh28JIXDqsOTPHH2qLJj0rNfVIsZHBAk4ElpF7sDPwsRROEW+1QK8b
35090 RaVK7362rPKgH1g/EkZgPI2h4H3PVz4zHvtH8aoVlwdVZqW1LS7YgFmypw23RuwhY/81q6UCzyr0
35091 TP579ZRdhE2o8mCP2w4lPJ9zcc+U30rq299yOIzzlr3xF7zSujtFWsan9sYXiwGd/BmoKoMWuDpI
35092 /k4+oKsGGelT84ATB+0tvz8KPFUgOSwsAGl0lUq8ILKpeeUYiZGo3BxN77t+Nwtd/jmliFKMAGzs
35093 GHxBvfaLdXe6YJ2E5/4tAgMBAAGjQjBAMA8GA1UdEwEB/wQFMAMBAf8wDgYDVR0PAQH/BAQDAgEG
35094 MB0GA1UdDgQWBBQVOIMPPyw/cDMezUb+B4wg4NfDtzANBgkqhkiG9w0BAQUFAAOCAQEAX/FBfXxc
35095 CLkr4NWSR/pnXKUTwwMhmytMiUbPWU3J/qVAtmPN3XEolWcRzCSs00Rsca4BIGsDoo8Ytyk6feUW
35096 YFN4PMCvFYP3j1IzJL1kk5fui/fbGKhtcbP3LBfQdCVp9/5rPJS+TUtBjE7ic9DjkCJzQ83z7+pz
35097 zkWKsKZJ/0x9nXGIxHYdkFsd7v3M9+79YKWxehZx0RbQfBI8bGmX265fOZpwLwU8GUYEmSA20GBu
35098 YQa7FkKMcPcw++DbZqMAAb3mLNqRX6BGi01qnD093QVG/na/oAo85ADmJ7f/hC3euiInlhBx6yLt
35099 398znM/jra6O1I7mT1GvFpLgXPYHDw==
35100 -----END CERTIFICATE-----
35101
35102 Certum Root CA
35103 ==============
35104 -----BEGIN CERTIFICATE-----
35105 MIIDDDCCAfSgAwIBAgIDAQAgMA0GCSqGSIb3DQEBBQUAMD4xCzAJBgNVBAYTAlBMMRswGQYDVQQK
35106 ExJVbml6ZXRvIFNwLiB6IG8uby4xEjAQBgNVBAMTCUNlcnR1bSBDQTAeFw0wMjA2MTExMDQ2Mzla
35107 Fw0yNzA2MTExMDQ2MzlaMD4xCzAJBgNVBAYTAlBMMRswGQYDVQQKExJVbml6ZXRvIFNwLiB6IG8u
35108 by4xEjAQBgNVBAMTCUNlcnR1bSBDQTCCASIwDQYJKoZIhvcNAQEBBQADggEPADCCAQoCggEBAM6x
35109 wS7TT3zNJc4YPk/EjG+AanPIW1H4m9LcuwBcsaD8dQPugfCI7iNS6eYVM42sLQnFdvkrOYCJ5JdL
35110 kKWoePhzQ3ukYbDYWMzhbGZ+nPMJXlVjhNWo7/OxLjBos8Q82KxujZlakE403Daaj4GIULdtlkIJ
35111 89eVgw1BS7Bqa/j8D35in2fE7SZfECYPCE/wpFcozo+47UX2bu4lXapuOb7kky/ZR6By6/qmW6/K
35112 Uz/iDsaWVhFu9+lmqSbYf5VT7QqFiLpPKaVCjF62/IUgAKpoC6EahQGcxEZjgoi2IrHu/qpGWX7P
35113 NSzVttpd90gzFFS269lvzs2I1qsb2pY7HVkCAwEAAaMTMBEwDwYDVR0TAQH/BAUwAwEB/zANBgkq
35114 hkiG9w0BAQUFAAOCAQEAuI3O7+cUus/usESSbLQ5PqKEbq24IXfS1HeCh+YgQYHu4vgRt2PRFze+
35115 GXYkHAQaTOs9qmdvLdTN/mUxcMUbpgIKumB7bVjCmkn+YzILa+M6wKyrO7Do0wlRjBCDxjTgxSvg
35116 GrZgFCdsMneMvLJymM/NzD+5yCRCFNZX/OYmQ6kd5YCQzgNUKD73P9P4Te1qCjqTE5s7FCMTY5w/
35117 0YcneeVMUeMBrYVdGjux1XMQpNPyvG5k9VpWkKjHDkx0Dy5xO/fIR/RpbxXyEV6DHpx8Uq79AtoS
35118 qFlnGNu8cN2bsWntgM6JQEhqDjXKKWYVIZQs6GAqm4VKQPNriiTsBhYscw==
35119 -----END CERTIFICATE-----
35120
35121 Comodo AAA Services root
35122 ========================
35123 -----BEGIN CERTIFICATE-----
35124 MIIEMjCCAxqgAwIBAgIBATANBgkqhkiG9w0BAQUFADB7MQswCQYDVQQGEwJHQjEbMBkGA1UECAwS
35125 R3JlYXRlciBNYW5jaGVzdGVyMRAwDgYDVQQHDAdTYWxmb3JkMRowGAYDVQQKDBFDb21vZG8gQ0Eg
35126 TGltaXRlZDEhMB8GA1UEAwwYQUFBIENlcnRpZmljYXRlIFNlcnZpY2VzMB4XDTA0MDEwMTAwMDAw
35127 MFoXDTI4MTIzMTIzNTk1OVowezELMAkGA1UEBhMCR0IxGzAZBgNVBAgMEkdyZWF0ZXIgTWFuY2hl
35128 c3RlcjEQMA4GA1UEBwwHU2FsZm9yZDEaMBgGA1UECgwRQ29tb2RvIENBIExpbWl0ZWQxITAfBgNV
35129 BAMMGEFBQSBDZXJ0aWZpY2F0ZSBTZXJ2aWNlczCCASIwDQYJKoZIhvcNAQEBBQADggEPADCCAQoC
35130 ggEBAL5AnfRu4ep2hxxNRUSOvkbIgwadwSr+GB+O5AL686tdUIoWMQuaBtDFcCLNSS1UY8y2bmhG
35131 C1Pqy0wkwLxyTurxFa70VJoSCsN6sjNg4tqJVfMiWPPe3M/vg4aijJRPn2jymJBGhCfHdr/jzDUs
35132 i14HZGWCwEiwqJH5YZ92IFCokcdmtet4YgNW8IoaE+oxox6gmf049vYnMlhvB/VruPsUK6+3qszW
35133 Y19zjNoFmag4qMsXeDZRrOme9Hg6jc8P2ULimAyrL58OAd7vn5lJ8S3frHRNG5i1R8XlKdH5kBjH
35134 Ypy+g8cmez6KJcfA3Z3mNWgQIJ2P2N7Sw4ScDV7oL8kCAwEAAaOBwDCBvTAdBgNVHQ4EFgQUoBEK
35135 Iz6W8Qfs4q8p74Klf9AwpLQwDgYDVR0PAQH/BAQDAgEGMA8GA1UdEwEB/wQFMAMBAf8wewYDVR0f
35136 BHQwcjA4oDagNIYyaHR0cDovL2NybC5jb21vZG9jYS5jb20vQUFBQ2VydGlmaWNhdGVTZXJ2aWNl
35137 cy5jcmwwNqA0oDKGMGh0dHA6Ly9jcmwuY29tb2RvLm5ldC9BQUFDZXJ0aWZpY2F0ZVNlcnZpY2Vz
35138 LmNybDANBgkqhkiG9w0BAQUFAAOCAQEACFb8AvCb6P+k+tZ7xkSAzk/ExfYAWMymtrwUSWgEdujm
35139 7l3sAg9g1o1QGE8mTgHj5rCl7r+8dFRBv/38ErjHT1r0iWAFf2C3BUrz9vHCv8S5dIa2LX1rzNLz
35140 Rt0vxuBqw8M0Ayx9lt1awg6nCpnBBYurDC/zXDrPbDdVCYfeU0BsWO/8tqtlbgT2G9w84FoVxp7Z
35141 8VlIMCFlA2zs6SFz7JsDoeA3raAVGI/6ugLOpyypEBMs1OUIJqsil2D4kF501KKaU73yqWjgom7C
35142 12yxow+ev+to51byrvLjKzg6CYG1a4XXvi3tPxq3smPi9WIsgtRqAEFQ8TmDn5XpNpaYbg==
35143 -----END CERTIFICATE-----
35144
35145 Comodo Secure Services root
35146 ===========================
35147 -----BEGIN CERTIFICATE-----
35148 MIIEPzCCAyegAwIBAgIBATANBgkqhkiG9w0BAQUFADB+MQswCQYDVQQGEwJHQjEbMBkGA1UECAwS
35149 R3JlYXRlciBNYW5jaGVzdGVyMRAwDgYDVQQHDAdTYWxmb3JkMRowGAYDVQQKDBFDb21vZG8gQ0Eg
35150 TGltaXRlZDEkMCIGA1UEAwwbU2VjdXJlIENlcnRpZmljYXRlIFNlcnZpY2VzMB4XDTA0MDEwMTAw
35151 MDAwMFoXDTI4MTIzMTIzNTk1OVowfjELMAkGA1UEBhMCR0IxGzAZBgNVBAgMEkdyZWF0ZXIgTWFu
35152 Y2hlc3RlcjEQMA4GA1UEBwwHU2FsZm9yZDEaMBgGA1UECgwRQ29tb2RvIENBIExpbWl0ZWQxJDAi
35153 BgNVBAMMG1NlY3VyZSBDZXJ0aWZpY2F0ZSBTZXJ2aWNlczCCASIwDQYJKoZIhvcNAQEBBQADggEP
35154 ADCCAQoCggEBAMBxM4KK0HDrc4eCQNUd5MvJDkKQ+d40uaG6EfQlhfPMcm3ye5drswfxdySRXyWP
35155 9nQ95IDC+DwN879A6vfIUtFyb+/Iq0G4bi4XKpVpDM3SHpR7LZQdqnXXs5jLrLxkU0C8j6ysNstc
35156 rbvd4JQX7NFc0L/vpZXJkMWwrPsbQ996CF23uPJAGysnnlDOXmWCiIxe004MeuoIkbY2qitC++rC
35157 oznl2yY4rYsK7hljxxwk3wN42ubqwUcaCwtGCd0C/N7Lh1/XMGNooa7cMqG6vv5Eq2i2pRcV/b3V
35158 p6ea5EQz6YiO/O1R65NxTq0B50SOqy3LqP4BSUjwwN3HaNiS/j0CAwEAAaOBxzCBxDAdBgNVHQ4E
35159 FgQUPNiTiMLAggnMAZkGkyDpnnAJY08wDgYDVR0PAQH/BAQDAgEGMA8GA1UdEwEB/wQFMAMBAf8w
35160 gYEGA1UdHwR6MHgwO6A5oDeGNWh0dHA6Ly9jcmwuY29tb2RvY2EuY29tL1NlY3VyZUNlcnRpZmlj
35161 YXRlU2VydmljZXMuY3JsMDmgN6A1hjNodHRwOi8vY3JsLmNvbW9kby5uZXQvU2VjdXJlQ2VydGlm
35162 aWNhdGVTZXJ2aWNlcy5jcmwwDQYJKoZIhvcNAQEFBQADggEBAIcBbSMdflsXfcFhMs+P5/OKlFlm
35163 4J4oqF7Tt/Q05qo5spcWxYJvMqTpjOev/e/C6LlLqqP05tqNZSH7uoDrJiiFGv45jN5bBAS0VPmj
35164 Z55B+glSzAVIqMk/IQQezkhr/IXownuvf7fM+F86/TXGDe+X3EyrEeFryzHRbPtIgKvcnDe4IRRL
35165 DXE97IMzbtFuMhbsmMcWi1mmNKsFVy2T96oTy9IT4rcuO81rUBcJaD61JlfutuC23bkpgHl9j6Pw
35166 pCikFcSF9CfUa7/lXORlAnZUtOM3ZiTTGWHIUhDlizeauan5Hb/qmZJhlv8BzaFfDbxxvA6sCx1H
35167 RR3B7Hzs/Sk=
35168 -----END CERTIFICATE-----
35169
35170 Comodo Trusted Services root
35171 ============================
35172 -----BEGIN CERTIFICATE-----
35173 MIIEQzCCAyugAwIBAgIBATANBgkqhkiG9w0BAQUFADB/MQswCQYDVQQGEwJHQjEbMBkGA1UECAwS
35174 R3JlYXRlciBNYW5jaGVzdGVyMRAwDgYDVQQHDAdTYWxmb3JkMRowGAYDVQQKDBFDb21vZG8gQ0Eg
35175 TGltaXRlZDElMCMGA1UEAwwcVHJ1c3RlZCBDZXJ0aWZpY2F0ZSBTZXJ2aWNlczAeFw0wNDAxMDEw
35176 MDAwMDBaFw0yODEyMzEyMzU5NTlaMH8xCzAJBgNVBAYTAkdCMRswGQYDVQQIDBJHcmVhdGVyIE1h
35177 bmNoZXN0ZXIxEDAOBgNVBAcMB1NhbGZvcmQxGjAYBgNVBAoMEUNvbW9kbyBDQSBMaW1pdGVkMSUw
35178 IwYDVQQDDBxUcnVzdGVkIENlcnRpZmljYXRlIFNlcnZpY2VzMIIBIjANBgkqhkiG9w0BAQEFAAOC
35179 AQ8AMIIBCgKCAQEA33FvNlhTWvI2VFeAxHQIIO0Yfyod5jWaHiWsnOWWfnJSoBVC21ndZHoa0Lh7
35180 3TkVvFVIxO06AOoxEbrycXQaZ7jPM8yoMa+j49d/vzMtTGo87IvDktJTdyR0nAducPy9C1t2ul/y
35181 /9c3S0pgePfw+spwtOpZqqPOSC+pw7ILfhdyFgymBwwbOM/JYrc/oJOlh0Hyt3BAd9i+FHzjqMB6
35182 juljatEPmsbS9Is6FARW1O24zG71++IsWL1/T2sr92AkWCTOJu80kTrV44HQsvAEAtdbtz6SrGsS
35183 ivnkBbA7kUlcsutT6vifR4buv5XAwAaf0lteERv0xwQ1KdJVXOTt6wIDAQABo4HJMIHGMB0GA1Ud
35184 DgQWBBTFe1i97doladL3WRaoszLAeydb9DAOBgNVHQ8BAf8EBAMCAQYwDwYDVR0TAQH/BAUwAwEB
35185 /zCBgwYDVR0fBHwwejA8oDqgOIY2aHR0cDovL2NybC5jb21vZG9jYS5jb20vVHJ1c3RlZENlcnRp
35186 ZmljYXRlU2VydmljZXMuY3JsMDqgOKA2hjRodHRwOi8vY3JsLmNvbW9kby5uZXQvVHJ1c3RlZENl
35187 cnRpZmljYXRlU2VydmljZXMuY3JsMA0GCSqGSIb3DQEBBQUAA4IBAQDIk4E7ibSvuIQSTI3S8Ntw
35188 uleGFTQQuS9/HrCoiWChisJ3DFBKmwCL2Iv0QeLQg4pKHBQGsKNoBXAxMKdTmw7pSqBYaWcOrp32
35189 pSxBvzwGa+RZzG0Q8ZZvH9/0BAKkn0U+yNj6NkZEUD+Cl5EfKNsYEYwq5GWDVxISjBc/lDb+XbDA
35190 BHcTuPQV1T84zJQ6VdCsmPW6AF/ghhmBeC8owH7TzEIK9a5QoNE+xqFx7D+gIIxmOom0jtTYsU0l
35191 R+4viMi14QVFwL4Ucd56/Y57fU0IlqUSc/AtyjcndBInTMu2l+nZrghtWjlA3QVHdWpaIbOjGM9O
35192 9y5Xt5hwXsjEeLBi
35193 -----END CERTIFICATE-----
35194
35195 QuoVadis Root CA
35196 ================
35197 -----BEGIN CERTIFICATE-----
35198 MIIF0DCCBLigAwIBAgIEOrZQizANBgkqhkiG9w0BAQUFADB/MQswCQYDVQQGEwJCTTEZMBcGA1UE
35199 ChMQUXVvVmFkaXMgTGltaXRlZDElMCMGA1UECxMcUm9vdCBDZXJ0aWZpY2F0aW9uIEF1dGhvcml0
35200 eTEuMCwGA1UEAxMlUXVvVmFkaXMgUm9vdCBDZXJ0aWZpY2F0aW9uIEF1dGhvcml0eTAeFw0wMTAz
35201 MTkxODMzMzNaFw0yMTAzMTcxODMzMzNaMH8xCzAJBgNVBAYTAkJNMRkwFwYDVQQKExBRdW9WYWRp
35202 cyBMaW1pdGVkMSUwIwYDVQQLExxSb290IENlcnRpZmljYXRpb24gQXV0aG9yaXR5MS4wLAYDVQQD
35203 EyVRdW9WYWRpcyBSb290IENlcnRpZmljYXRpb24gQXV0aG9yaXR5MIIBIjANBgkqhkiG9w0BAQEF
35204 AAOCAQ8AMIIBCgKCAQEAv2G1lVO6V/z68mcLOhrfEYBklbTRvM16z/Ypli4kVEAkOPcahdxYTMuk
35205 J0KX0J+DisPkBgNbAKVRHnAEdOLB1Dqr1607BxgFjv2DrOpm2RgbaIr1VxqYuvXtdj182d6UajtL
35206 F8HVj71lODqV0D1VNk7feVcxKh7YWWVJWCCYfqtffp/p1k3sg3Spx2zY7ilKhSoGFPlU5tPaZQeL
35207 YzcS19Dsw3sgQUSj7cugF+FxZc4dZjH3dgEZyH0DWLaVSR2mEiboxgx24ONmy+pdpibu5cxfvWen
35208 AScOospUxbF6lR1xHkopigPcakXBpBlebzbNw6Kwt/5cOOJSvPhEQ+aQuwIDAQABo4ICUjCCAk4w
35209 PQYIKwYBBQUHAQEEMTAvMC0GCCsGAQUFBzABhiFodHRwczovL29jc3AucXVvdmFkaXNvZmZzaG9y
35210 ZS5jb20wDwYDVR0TAQH/BAUwAwEB/zCCARoGA1UdIASCAREwggENMIIBCQYJKwYBBAG+WAABMIH7
35211 MIHUBggrBgEFBQcCAjCBxxqBxFJlbGlhbmNlIG9uIHRoZSBRdW9WYWRpcyBSb290IENlcnRpZmlj
35212 YXRlIGJ5IGFueSBwYXJ0eSBhc3N1bWVzIGFjY2VwdGFuY2Ugb2YgdGhlIHRoZW4gYXBwbGljYWJs
35213 ZSBzdGFuZGFyZCB0ZXJtcyBhbmQgY29uZGl0aW9ucyBvZiB1c2UsIGNlcnRpZmljYXRpb24gcHJh
35214 Y3RpY2VzLCBhbmQgdGhlIFF1b1ZhZGlzIENlcnRpZmljYXRlIFBvbGljeS4wIgYIKwYBBQUHAgEW
35215 Fmh0dHA6Ly93d3cucXVvdmFkaXMuYm0wHQYDVR0OBBYEFItLbe3TKbkGGew5Oanwl4Rqy+/fMIGu
35216 BgNVHSMEgaYwgaOAFItLbe3TKbkGGew5Oanwl4Rqy+/foYGEpIGBMH8xCzAJBgNVBAYTAkJNMRkw
35217 FwYDVQQKExBRdW9WYWRpcyBMaW1pdGVkMSUwIwYDVQQLExxSb290IENlcnRpZmljYXRpb24gQXV0
35218 aG9yaXR5MS4wLAYDVQQDEyVRdW9WYWRpcyBSb290IENlcnRpZmljYXRpb24gQXV0aG9yaXR5ggQ6
35219 tlCLMA4GA1UdDwEB/wQEAwIBBjANBgkqhkiG9w0BAQUFAAOCAQEAitQUtf70mpKnGdSkfnIYj9lo
35220 fFIk3WdvOXrEql494liwTXCYhGHoG+NpGA7O+0dQoE7/8CQfvbLO9Sf87C9TqnN7Az10buYWnuul
35221 LsS/VidQK2K6vkscPFVcQR0kvoIgR13VRH56FmjffU1RcHhXHTMe/QKZnAzNCgVPx7uOpHX6Sm2x
35222 gI4JVrmcGmD+XcHXetwReNDWXcG31a0ymQM6isxUJTkxgXsTIlG6Rmyhu576BGxJJnSP0nPrzDCi
35223 5upZIof4l/UO/erMkqQWxFIY6iHOsfHmhIHluqmGKPJDWl0Snawe2ajlCmqnf6CHKc/yiU3U7MXi
35224 5nrQNiOKSnQ2+Q==
35225 -----END CERTIFICATE-----
35226
35227 QuoVadis Root CA 2
35228 ==================
35229 -----BEGIN CERTIFICATE-----
35230 MIIFtzCCA5+gAwIBAgICBQkwDQYJKoZIhvcNAQEFBQAwRTELMAkGA1UEBhMCQk0xGTAXBgNVBAoT
35231 EFF1b1ZhZGlzIExpbWl0ZWQxGzAZBgNVBAMTElF1b1ZhZGlzIFJvb3QgQ0EgMjAeFw0wNjExMjQx
35232 ODI3MDBaFw0zMTExMjQxODIzMzNaMEUxCzAJBgNVBAYTAkJNMRkwFwYDVQQKExBRdW9WYWRpcyBM
35233 aW1pdGVkMRswGQYDVQQDExJRdW9WYWRpcyBSb290IENBIDIwggIiMA0GCSqGSIb3DQEBAQUAA4IC
35234 DwAwggIKAoICAQCaGMpLlA0ALa8DKYrwD4HIrkwZhR0In6spRIXzL4GtMh6QRr+jhiYaHv5+HBg6
35235 XJxgFyo6dIMzMH1hVBHL7avg5tKifvVrbxi3Cgst/ek+7wrGsxDp3MJGF/hd/aTa/55JWpzmM+Yk
35236 lvc/ulsrHHo1wtZn/qtmUIttKGAr79dgw8eTvI02kfN/+NsRE8Scd3bBrrcCaoF6qUWD4gXmuVbB
35237 lDePSHFjIuwXZQeVikvfj8ZaCuWw419eaxGrDPmF60Tp+ARz8un+XJiM9XOva7R+zdRcAitMOeGy
35238 lZUtQofX1bOQQ7dsE/He3fbE+Ik/0XX1ksOR1YqI0JDs3G3eicJlcZaLDQP9nL9bFqyS2+r+eXyt
35239 66/3FsvbzSUr5R/7mp/iUcw6UwxI5g69ybR2BlLmEROFcmMDBOAENisgGQLodKcftslWZvB1Jdxn
35240 wQ5hYIizPtGo/KPaHbDRsSNU30R2be1B2MGyIrZTHN81Hdyhdyox5C315eXbyOD/5YDXC2Og/zOh
35241 D7osFRXql7PSorW+8oyWHhqPHWykYTe5hnMz15eWniN9gqRMgeKh0bpnX5UHoycR7hYQe7xFSkyy
35242 BNKr79X9DFHOUGoIMfmR2gyPZFwDwzqLID9ujWc9Otb+fVuIyV77zGHcizN300QyNQliBJIWENie
35243 J0f7OyHj+OsdWwIDAQABo4GwMIGtMA8GA1UdEwEB/wQFMAMBAf8wCwYDVR0PBAQDAgEGMB0GA1Ud
35244 DgQWBBQahGK8SEwzJQTU7tD2A8QZRtGUazBuBgNVHSMEZzBlgBQahGK8SEwzJQTU7tD2A8QZRtGU
35245 a6FJpEcwRTELMAkGA1UEBhMCQk0xGTAXBgNVBAoTEFF1b1ZhZGlzIExpbWl0ZWQxGzAZBgNVBAMT
35246 ElF1b1ZhZGlzIFJvb3QgQ0EgMoICBQkwDQYJKoZIhvcNAQEFBQADggIBAD4KFk2fBluornFdLwUv
35247 Z+YTRYPENvbzwCYMDbVHZF34tHLJRqUDGCdViXh9duqWNIAXINzng/iN/Ae42l9NLmeyhP3ZRPx3
35248 UIHmfLTJDQtyU/h2BwdBR5YM++CCJpNVjP4iH2BlfF/nJrP3MpCYUNQ3cVX2kiF495V5+vgtJodm
35249 VjB3pjd4M1IQWK4/YY7yarHvGH5KWWPKjaJW1acvvFYfzznB4vsKqBUsfU16Y8Zsl0Q80m/DShcK
35250 +JDSV6IZUaUtl0HaB0+pUNqQjZRG4T7wlP0QADj1O+hA4bRuVhogzG9Yje0uRY/W6ZM/57Es3zrW
35251 IozchLsib9D45MY56QSIPMO661V6bYCZJPVsAfv4l7CUW+v90m/xd2gNNWQjrLhVoQPRTUIZ3Ph1
35252 WVaj+ahJefivDrkRoHy3au000LYmYjgahwz46P0u05B/B5EqHdZ+XIWDmbA4CD/pXvk1B+TJYm5X
35253 f6dQlfe6yJvmjqIBxdZmv3lh8zwc4bmCXF2gw+nYSL0ZohEUGW6yhhtoPkg3Goi3XZZenMfvJ2II
35254 4pEZXNLxId26F0KCl3GBUzGpn/Z9Yr9y4aOTHcyKJloJONDO1w2AFrR4pTqHTI2KpdVGl/IsELm8
35255 VCLAAVBpQ570su9t+Oza8eOx79+Rj1QqCyXBJhnEUhAFZdWCEOrCMc0u
35256 -----END CERTIFICATE-----
35257
35258 QuoVadis Root CA 3
35259 ==================
35260 -----BEGIN CERTIFICATE-----
35261 MIIGnTCCBIWgAwIBAgICBcYwDQYJKoZIhvcNAQEFBQAwRTELMAkGA1UEBhMCQk0xGTAXBgNVBAoT
35262 EFF1b1ZhZGlzIExpbWl0ZWQxGzAZBgNVBAMTElF1b1ZhZGlzIFJvb3QgQ0EgMzAeFw0wNjExMjQx
35263 OTExMjNaFw0zMTExMjQxOTA2NDRaMEUxCzAJBgNVBAYTAkJNMRkwFwYDVQQKExBRdW9WYWRpcyBM
35264 aW1pdGVkMRswGQYDVQQDExJRdW9WYWRpcyBSb290IENBIDMwggIiMA0GCSqGSIb3DQEBAQUAA4IC
35265 DwAwggIKAoICAQDMV0IWVJzmmNPTTe7+7cefQzlKZbPoFog02w1ZkXTPkrgEQK0CSzGrvI2RaNgg
35266 DhoB4hp7Thdd4oq3P5kazethq8Jlph+3t723j/z9cI8LoGe+AaJZz3HmDyl2/7FWeUUrH556VOij
35267 KTVopAFPD6QuN+8bv+OPEKhyq1hX51SGyMnzW9os2l2ObjyjPtr7guXd8lyyBTNvijbO0BNO/79K
35268 DDRMpsMhvVAEVeuxu537RR5kFd5VAYwCdrXLoT9CabwvvWhDFlaJKjdhkf2mrk7AyxRllDdLkgbv
35269 BNDInIjbC3uBr7E9KsRlOni27tyAsdLTmZw67mtaa7ONt9XOnMK+pUsvFrGeaDsGb659n/je7Mwp
35270 p5ijJUMv7/FfJuGITfhebtfZFG4ZM2mnO4SJk8RTVROhUXhA+LjJou57ulJCg54U7QVSWllWp5f8
35271 nT8KKdjcT5EOE7zelaTfi5m+rJsziO+1ga8bxiJTyPbH7pcUsMV8eFLI8M5ud2CEpukqdiDtWAEX
35272 MJPpGovgc2PZapKUSU60rUqFxKMiMPwJ7Wgic6aIDFUhWMXhOp8q3crhkODZc6tsgLjoC2SToJyM
35273 Gf+z0gzskSaHirOi4XCPLArlzW1oUevaPwV/izLmE1xr/l9A4iLItLRkT9a6fUg+qGkM17uGcclz
35274 uD87nSVL2v9A6wIDAQABo4IBlTCCAZEwDwYDVR0TAQH/BAUwAwEB/zCB4QYDVR0gBIHZMIHWMIHT
35275 BgkrBgEEAb5YAAMwgcUwgZMGCCsGAQUFBwICMIGGGoGDQW55IHVzZSBvZiB0aGlzIENlcnRpZmlj
35276 YXRlIGNvbnN0aXR1dGVzIGFjY2VwdGFuY2Ugb2YgdGhlIFF1b1ZhZGlzIFJvb3QgQ0EgMyBDZXJ0
35277 aWZpY2F0ZSBQb2xpY3kgLyBDZXJ0aWZpY2F0aW9uIFByYWN0aWNlIFN0YXRlbWVudC4wLQYIKwYB
35278 BQUHAgEWIWh0dHA6Ly93d3cucXVvdmFkaXNnbG9iYWwuY29tL2NwczALBgNVHQ8EBAMCAQYwHQYD
35279 VR0OBBYEFPLAE+CCQz777i9nMpY1XNu4ywLQMG4GA1UdIwRnMGWAFPLAE+CCQz777i9nMpY1XNu4
35280 ywLQoUmkRzBFMQswCQYDVQQGEwJCTTEZMBcGA1UEChMQUXVvVmFkaXMgTGltaXRlZDEbMBkGA1UE
35281 AxMSUXVvVmFkaXMgUm9vdCBDQSAzggIFxjANBgkqhkiG9w0BAQUFAAOCAgEAT62gLEz6wPJv92ZV
35282 qyM07ucp2sNbtrCD2dDQ4iH782CnO11gUyeim/YIIirnv6By5ZwkajGxkHon24QRiSemd1o417+s
35283 hvzuXYO8BsbRd2sPbSQvS3pspweWyuOEn62Iix2rFo1bZhfZFvSLgNLd+LJ2w/w4E6oM3kJpK27z
35284 POuAJ9v1pkQNn1pVWQvVDVJIxa6f8i+AxeoyUDUSly7B4f/xI4hROJ/yZlZ25w9Rl6VSDE1JUZU2
35285 Pb+iSwwQHYaZTKrzchGT5Or2m9qoXadNt54CrnMAyNojA+j56hl0YgCUyyIgvpSnWbWCar6ZeXqp
35286 8kokUvd0/bpO5qgdAm6xDYBEwa7TIzdfu4V8K5Iu6H6li92Z4b8nby1dqnuH/grdS/yO9SbkbnBC
35287 bjPsMZ57k8HkyWkaPcBrTiJt7qtYTcbQQcEr6k8Sh17rRdhs9ZgC06DYVYoGmRmioHfRMJ6szHXu
35288 g/WwYjnPbFfiTNKRCw51KBuav/0aQ/HKd/s7j2G4aSgWQgRecCocIdiP4b0jWy10QJLZYxkNc91p
35289 vGJHvOB0K7Lrfb5BG7XARsWhIstfTsEokt4YutUqKLsRixeTmJlglFwjz1onl14LBQaTNx47aTbr
35290 qZ5hHY8y2o4M1nQ+ewkk2gF3R8Q7zTSMmfXK4SVhM7JZG+Ju1zdXtg2pEto=
35291 -----END CERTIFICATE-----
35292
35293 Security Communication Root CA
35294 ==============================
35295 -----BEGIN CERTIFICATE-----
35296 MIIDWjCCAkKgAwIBAgIBADANBgkqhkiG9w0BAQUFADBQMQswCQYDVQQGEwJKUDEYMBYGA1UEChMP
35297 U0VDT00gVHJ1c3QubmV0MScwJQYDVQQLEx5TZWN1cml0eSBDb21tdW5pY2F0aW9uIFJvb3RDQTEw
35298 HhcNMDMwOTMwMDQyMDQ5WhcNMjMwOTMwMDQyMDQ5WjBQMQswCQYDVQQGEwJKUDEYMBYGA1UEChMP
35299 U0VDT00gVHJ1c3QubmV0MScwJQYDVQQLEx5TZWN1cml0eSBDb21tdW5pY2F0aW9uIFJvb3RDQTEw
35300 ggEiMA0GCSqGSIb3DQEBAQUAA4IBDwAwggEKAoIBAQCzs/5/022x7xZ8V6UMbXaKL0u/ZPtM7orw
35301 8yl89f/uKuDp6bpbZCKamm8sOiZpUQWZJtzVHGpxxpp9Hp3dfGzGjGdnSj74cbAZJ6kJDKaVv0uM
35302 DPpVmDvY6CKhS3E4eayXkmmziX7qIWgGmBSWh9JhNrxtJ1aeV+7AwFb9Ms+k2Y7CI9eNqPPYJayX
35303 5HA49LY6tJ07lyZDo6G8SVlyTCMwhwFY9k6+HGhWZq/NQV3Is00qVUarH9oe4kA92819uZKAnDfd
35304 DJZkndwi92SL32HeFZRSFaB9UslLqCHJxrHty8OVYNEP8Ktw+N/LTX7s1vqr2b1/VPKl6Xn62dZ2
35305 JChzAgMBAAGjPzA9MB0GA1UdDgQWBBSgc0mZaNyFW2XjmygvV5+9M7wHSDALBgNVHQ8EBAMCAQYw
35306 DwYDVR0TAQH/BAUwAwEB/zANBgkqhkiG9w0BAQUFAAOCAQEAaECpqLvkT115swW1F7NgE+vGkl3g
35307 0dNq/vu+m22/xwVtWSDEHPC32oRYAmP6SBbvT6UL90qY8j+eG61Ha2POCEfrUj94nK9NrvjVT8+a
35308 mCoQQTlSxN3Zmw7vkwGusi7KaEIkQmywszo+zenaSMQVy+n5Bw+SUEmK3TGXX8npN6o7WWWXlDLJ
35309 s58+OmJYxUmtYg5xpTKqL8aJdkNAExNnPaJUJRDL8Try2frbSVa7pv6nQTXD4IhhyYjH3zYQIphZ
35310 6rBK+1YWc26sTfcioU+tHXotRSflMMFe8toTyyVCUZVHA4xsIcx0Qu1T/zOLjw9XARYvz6buyXAi
35311 FL39vmwLAw==
35312 -----END CERTIFICATE-----
35313
35314 Sonera Class 1 Root CA
35315 ======================
35316 -----BEGIN CERTIFICATE-----
35317 MIIDIDCCAgigAwIBAgIBJDANBgkqhkiG9w0BAQUFADA5MQswCQYDVQQGEwJGSTEPMA0GA1UEChMG
35318 U29uZXJhMRkwFwYDVQQDExBTb25lcmEgQ2xhc3MxIENBMB4XDTAxMDQwNjEwNDkxM1oXDTIxMDQw
35319 NjEwNDkxM1owOTELMAkGA1UEBhMCRkkxDzANBgNVBAoTBlNvbmVyYTEZMBcGA1UEAxMQU29uZXJh
35320 IENsYXNzMSBDQTCCASIwDQYJKoZIhvcNAQEBBQADggEPADCCAQoCggEBALWJHytPZwp5/8Ue+H88
35321 7dF+2rDNbS82rDTG29lkFwhjMDMiikzujrsPDUJVyZ0upe/3p4zDq7mXy47vPxVnqIJyY1MPQYx9
35322 EJUkoVqlBvqSV536pQHydekfvFYmUk54GWVYVQNYwBSujHxVX3BbdyMGNpfzJLWaRpXk3w0LBUXl
35323 0fIdgrvGE+D+qnr9aTCU89JFhfzyMlsy3uhsXR/LpCJ0sICOXZT3BgBLqdReLjVQCfOAl/QMF645
35324 2F/NM8EcyonCIvdFEu1eEpOdY6uCLrnrQkFEy0oaAIINnvmLVz5MxxftLItyM19yejhW1ebZrgUa
35325 HXVFsculJRwSVzb9IjcCAwEAAaMzMDEwDwYDVR0TAQH/BAUwAwEB/zARBgNVHQ4ECgQIR+IMi/ZT
35326 iFIwCwYDVR0PBAQDAgEGMA0GCSqGSIb3DQEBBQUAA4IBAQCLGrLJXWG04bkruVPRsoWdd44W7hE9
35327 28Jj2VuXZfsSZ9gqXLar5V7DtxYvyOirHYr9qxp81V9jz9yw3Xe5qObSIjiHBxTZ/75Wtf0HDjxV
35328 yhbMp6Z3N/vbXB9OWQaHowND9Rart4S9Tu+fMTfwRvFAttEMpWT4Y14h21VOTzF2nBBhjrZTOqMR
35329 vq9tfB69ri3iDGnHhVNoomG6xT60eVR4ngrHAr5i0RGCS2UvkVrCqIexVmiUefkl98HVrhq4uz2P
35330 qYo4Ffdz0Fpg0YCw8NzVUM1O7pJIae2yIx4wzMiUyLb1O4Z/P6Yun/Y+LLWSlj7fLJOK/4GMDw9Z
35331 IRlXvVWa
35332 -----END CERTIFICATE-----
35333
35334 Sonera Class 2 Root CA
35335 ======================
35336 -----BEGIN CERTIFICATE-----
35337 MIIDIDCCAgigAwIBAgIBHTANBgkqhkiG9w0BAQUFADA5MQswCQYDVQQGEwJGSTEPMA0GA1UEChMG
35338 U29uZXJhMRkwFwYDVQQDExBTb25lcmEgQ2xhc3MyIENBMB4XDTAxMDQwNjA3Mjk0MFoXDTIxMDQw
35339 NjA3Mjk0MFowOTELMAkGA1UEBhMCRkkxDzANBgNVBAoTBlNvbmVyYTEZMBcGA1UEAxMQU29uZXJh
35340 IENsYXNzMiBDQTCCASIwDQYJKoZIhvcNAQEBBQADggEPADCCAQoCggEBAJAXSjWdyvANlsdE+hY3
35341 /Ei9vX+ALTU74W+oZ6m/AxxNjG8yR9VBaKQTBME1DJqEQ/xcHf+Js+gXGM2RX/uJ4+q/Tl18GybT
35342 dXnt5oTjV+WtKcT0OijnpXuENmmz/V52vaMtmdOQTiMofRhj8VQ7Jp12W5dCsv+u8E7s3TmVToMG
35343 f+dJQMjFAbJUWmYdPfz56TwKnoG4cPABi+QjVHzIrviQHgCWctRUz2EjvOr7nQKV0ba5cTppCD8P
35344 tOFCx4j1P5iop7oc4HFx71hXgVB6XGt0Rg6DA5jDjqhu8nYybieDwnPz3BjotJPqdURrBGAgcVeH
35345 nfO+oJAjPYok4doh28MCAwEAAaMzMDEwDwYDVR0TAQH/BAUwAwEB/zARBgNVHQ4ECgQISqCqWITT
35346 XjwwCwYDVR0PBAQDAgEGMA0GCSqGSIb3DQEBBQUAA4IBAQBazof5FnIVV0sd2ZvnoiYw7JNn39Yt
35347 0jSv9zilzqsWuasvfDXLrNAPtEwr/IDva4yRXzZ299uzGxnq9LIR/WFxRL8oszodv7ND6J+/3DEI
35348 cbCdjdY0RzKQxmUk96BKfARzjzlvF4xytb1LyHr4e4PDKE6cCepnP7JnBBvDFNr450kkkdAdavph
35349 Oe9r5yF1BgfYErQhIHBCcYHaPJo2vqZbDWpsmh+Re/n570K6Tk6ezAyNlNzZRZxe7EJQY670XcSx
35350 EtzKO6gunRRaBXW37Ndj4ro1tgQIkejanZz2ZrUYrAqmVCY0M9IbwdR/GjqOC6oybtv8TyWf2TLH
35351 llpwrN9M
35352 -----END CERTIFICATE-----
35353
35354 Staat der Nederlanden Root CA
35355 =============================
35356 -----BEGIN CERTIFICATE-----
35357 MIIDujCCAqKgAwIBAgIEAJiWijANBgkqhkiG9w0BAQUFADBVMQswCQYDVQQGEwJOTDEeMBwGA1UE
35358 ChMVU3RhYXQgZGVyIE5lZGVybGFuZGVuMSYwJAYDVQQDEx1TdGFhdCBkZXIgTmVkZXJsYW5kZW4g
35359 Um9vdCBDQTAeFw0wMjEyMTcwOTIzNDlaFw0xNTEyMTYwOTE1MzhaMFUxCzAJBgNVBAYTAk5MMR4w
35360 HAYDVQQKExVTdGFhdCBkZXIgTmVkZXJsYW5kZW4xJjAkBgNVBAMTHVN0YWF0IGRlciBOZWRlcmxh
35361 bmRlbiBSb290IENBMIIBIjANBgkqhkiG9w0BAQEFAAOCAQ8AMIIBCgKCAQEAmNK1URF6gaYUmHFt
35362 vsznExvWJw56s2oYHLZhWtVhCb/ekBPHZ+7d89rFDBKeNVU+LCeIQGv33N0iYfXCxw719tV2U02P
35363 jLwYdjeFnejKScfST5gTCaI+Ioicf9byEGW07l8Y1Rfj+MX94p2i71MOhXeiD+EwR+4A5zN9RGca
35364 C1Hoi6CeUJhoNFIfLm0B8mBF8jHrqTFoKbt6QZ7GGX+UtFE5A3+y3qcym7RHjm+0Sq7lr7HcsBth
35365 vJly3uSJt3omXdozSVtSnA71iq3DuD3oBmrC1SoLbHuEvVYFy4ZlkuxEK7COudxwC0barbxjiDn6
35366 22r+I/q85Ej0ZytqERAhSQIDAQABo4GRMIGOMAwGA1UdEwQFMAMBAf8wTwYDVR0gBEgwRjBEBgRV
35367 HSAAMDwwOgYIKwYBBQUHAgEWLmh0dHA6Ly93d3cucGtpb3ZlcmhlaWQubmwvcG9saWNpZXMvcm9v
35368 dC1wb2xpY3kwDgYDVR0PAQH/BAQDAgEGMB0GA1UdDgQWBBSofeu8Y6R0E3QA7Jbg0zTBLL9s+DAN
35369 BgkqhkiG9w0BAQUFAAOCAQEABYSHVXQ2YcG70dTGFagTtJ+k/rvuFbQvBgwp8qiSpGEN/KtcCFtR
35370 EytNwiphyPgJWPwtArI5fZlmgb9uXJVFIGzmeafR2Bwp/MIgJ1HI8XxdNGdphREwxgDS1/PTfLbw
35371 MVcoEoJz6TMvplW0C5GUR5z6u3pCMuiufi3IvKwUv9kP2Vv8wfl6leF9fpb8cbDCTMjfRTTJzg3y
35372 nGQI0DvDKcWy7ZAEwbEpkcUwb8GpcjPM/l0WFywRaed+/sWDCN+83CI6LiBpIzlWYGeQiy52OfsR
35373 iJf2fL1LuCAWZwWN4jvBcj+UlTfHXbme2JOhF4//DGYVwSR8MnwDHTuhWEUykw==
35374 -----END CERTIFICATE-----
35375
35376 TDC Internet Root CA
35377 ====================
35378 -----BEGIN CERTIFICATE-----
35379 MIIEKzCCAxOgAwIBAgIEOsylTDANBgkqhkiG9w0BAQUFADBDMQswCQYDVQQGEwJESzEVMBMGA1UE
35380 ChMMVERDIEludGVybmV0MR0wGwYDVQQLExRUREMgSW50ZXJuZXQgUm9vdCBDQTAeFw0wMTA0MDUx
35381 NjMzMTdaFw0yMTA0MDUxNzAzMTdaMEMxCzAJBgNVBAYTAkRLMRUwEwYDVQQKEwxUREMgSW50ZXJu
35382 ZXQxHTAbBgNVBAsTFFREQyBJbnRlcm5ldCBSb290IENBMIIBIjANBgkqhkiG9w0BAQEFAAOCAQ8A
35383 MIIBCgKCAQEAxLhAvJHVYx/XmaCLDEAedLdInUaMArLgJF/wGROnN4NrXceO+YQwzho7+vvOi20j
35384 xsNuZp+Jpd/gQlBn+h9sHvTQBda/ytZO5GhgbEaqHF1j4QeGDmUApy6mcca8uYGoOn0a0vnRrEvL
35385 znWv3Hv6gXPU/Lq9QYjUdLP5Xjg6PEOo0pVOd20TDJ2PeAG3WiAfAzc14izbSysseLlJ28TQx5yc
35386 5IogCSEWVmb/Bexb4/DPqyQkXsN/cHoSxNK1EKC2IeGNeGlVRGn1ypYcNIUXJXfi9i8nmHj9eQY6
35387 otZaQ8H/7AQ77hPv01ha/5Lr7K7a8jcDR0G2l8ktCkEiu7vmpwIDAQABo4IBJTCCASEwEQYJYIZI
35388 AYb4QgEBBAQDAgAHMGUGA1UdHwReMFwwWqBYoFakVDBSMQswCQYDVQQGEwJESzEVMBMGA1UEChMM
35389 VERDIEludGVybmV0MR0wGwYDVQQLExRUREMgSW50ZXJuZXQgUm9vdCBDQTENMAsGA1UEAxMEQ1JM
35390 MTArBgNVHRAEJDAigA8yMDAxMDQwNTE2MzMxN1qBDzIwMjEwNDA1MTcwMzE3WjALBgNVHQ8EBAMC
35391 AQYwHwYDVR0jBBgwFoAUbGQBx/2FbazI2p5QCIUItTxWqFAwHQYDVR0OBBYEFGxkAcf9hW2syNqe
35392 UAiFCLU8VqhQMAwGA1UdEwQFMAMBAf8wHQYJKoZIhvZ9B0EABBAwDhsIVjUuMDo0LjADAgSQMA0G
35393 CSqGSIb3DQEBBQUAA4IBAQBOQ8zR3R0QGwZ/t6T609lN+yOfI1Rb5osvBCiLtSdtiaHsmGnc540m
35394 gwV5dOy0uaOXwTUA/RXaOYE6lTGQ3pfphqiZdwzlWqCE/xIWrG64jcN7ksKsLtB9KOy282A4aW8+
35395 2ARVPp7MVdK6/rtHBNcK2RYKNCn1WBPVT8+PVkuzHu7TmHnaCB4Mb7j4Fifvwm899qNLPg7kbWzb
35396 O0ESm70NRyN/PErQr8Cv9u8btRXE64PECV90i9kR+8JWsTz4cMo0jUNAE4z9mQNUecYu6oah9jrU
35397 Cbz0vGbMPVjQV0kK7iXiQe4T+Zs4NNEA9X7nlB38aQNiuJkFBT1reBK9sG9l
35398 -----END CERTIFICATE-----
35399
35400 TDC OCES Root CA
35401 ================
35402 -----BEGIN CERTIFICATE-----
35403 MIIFGTCCBAGgAwIBAgIEPki9xDANBgkqhkiG9w0BAQUFADAxMQswCQYDVQQGEwJESzEMMAoGA1UE
35404 ChMDVERDMRQwEgYDVQQDEwtUREMgT0NFUyBDQTAeFw0wMzAyMTEwODM5MzBaFw0zNzAyMTEwOTA5
35405 MzBaMDExCzAJBgNVBAYTAkRLMQwwCgYDVQQKEwNUREMxFDASBgNVBAMTC1REQyBPQ0VTIENBMIIB
35406 IjANBgkqhkiG9w0BAQEFAAOCAQ8AMIIBCgKCAQEArGL2YSCyz8DGhdfjeebM7fI5kqSXLmSjhFuH
35407 nEz9pPPEXyG9VhDr2y5h7JNp46PMvZnDBfwGuMo2HP6QjklMxFaaL1a8z3sM8W9Hpg1DTeLpHTk0
35408 zY0s2RKY+ePhwUp8hjjEqcRhiNJerxomTdXkoCJHhNlktxmW/OwZ5LKXJk5KTMuPJItUGBxIYXvV
35409 iGjaXbXqzRowwYCDdlCqT9HU3Tjw7xb04QxQBr/q+3pJoSgrHPb8FTKjdGqPqcNiKXEx5TukYBde
35410 dObaE+3pHx8b0bJoc8YQNHVGEBDjkAB2QMuLt0MJIf+rTpPGWOmlgtt3xDqZsXKVSQTwtyv6e1mO
35411 3QIDAQABo4ICNzCCAjMwDwYDVR0TAQH/BAUwAwEB/zAOBgNVHQ8BAf8EBAMCAQYwgewGA1UdIASB
35412 5DCB4TCB3gYIKoFQgSkBAQEwgdEwLwYIKwYBBQUHAgEWI2h0dHA6Ly93d3cuY2VydGlmaWthdC5k
35413 ay9yZXBvc2l0b3J5MIGdBggrBgEFBQcCAjCBkDAKFgNUREMwAwIBARqBgUNlcnRpZmlrYXRlciBm
35414 cmEgZGVubmUgQ0EgdWRzdGVkZXMgdW5kZXIgT0lEIDEuMi4yMDguMTY5LjEuMS4xLiBDZXJ0aWZp
35415 Y2F0ZXMgZnJvbSB0aGlzIENBIGFyZSBpc3N1ZWQgdW5kZXIgT0lEIDEuMi4yMDguMTY5LjEuMS4x
35416 LjARBglghkgBhvhCAQEEBAMCAAcwgYEGA1UdHwR6MHgwSKBGoESkQjBAMQswCQYDVQQGEwJESzEM
35417 MAoGA1UEChMDVERDMRQwEgYDVQQDEwtUREMgT0NFUyBDQTENMAsGA1UEAxMEQ1JMMTAsoCqgKIYm
35418 aHR0cDovL2NybC5vY2VzLmNlcnRpZmlrYXQuZGsvb2Nlcy5jcmwwKwYDVR0QBCQwIoAPMjAwMzAy
35419 MTEwODM5MzBagQ8yMDM3MDIxMTA5MDkzMFowHwYDVR0jBBgwFoAUYLWF7FZkfhIZJ2cdUBVLc647
35420 +RIwHQYDVR0OBBYEFGC1hexWZH4SGSdnHVAVS3OuO/kSMB0GCSqGSIb2fQdBAAQQMA4bCFY2LjA6
35421 NC4wAwIEkDANBgkqhkiG9w0BAQUFAAOCAQEACromJkbTc6gJ82sLMJn9iuFXehHTuJTXCRBuo7E4
35422 A9G28kNBKWKnctj7fAXmMXAnVBhOinxO5dHKjHiIzxvTkIvmI/gLDjNDfZziChmPyQE+dF10yYsc
35423 A+UYyAFMP8uXBV2YcaaYb7Z8vTd/vuGTJW1v8AqtFxjhA7wHKcitJuj4YfD9IQl+mo6paH1IYnK9
35424 AOoBmbgGglGBTvH1tJFUuSN6AJqfXY3gPGS5GhKSKseCRHI53OI8xthV9RVOyAUO28bQYqbsFbS1
35425 AoLbrIyigfCbmTH1ICCoiGEKB5+U/NDXG8wuF/MEJ3Zn61SD/aSQfgY9BKNDLdr8C2LqL19iUw==
35426 -----END CERTIFICATE-----
35427
35428 UTN DATACorp SGC Root CA
35429 ========================
35430 -----BEGIN CERTIFICATE-----
35431 MIIEXjCCA0agAwIBAgIQRL4Mi1AAIbQR0ypoBqmtaTANBgkqhkiG9w0BAQUFADCBkzELMAkGA1UE
35432 BhMCVVMxCzAJBgNVBAgTAlVUMRcwFQYDVQQHEw5TYWx0IExha2UgQ2l0eTEeMBwGA1UEChMVVGhl
35433 IFVTRVJUUlVTVCBOZXR3b3JrMSEwHwYDVQQLExhodHRwOi8vd3d3LnVzZXJ0cnVzdC5jb20xGzAZ
35434 BgNVBAMTElVUTiAtIERBVEFDb3JwIFNHQzAeFw05OTA2MjQxODU3MjFaFw0xOTA2MjQxOTA2MzBa
35435 MIGTMQswCQYDVQQGEwJVUzELMAkGA1UECBMCVVQxFzAVBgNVBAcTDlNhbHQgTGFrZSBDaXR5MR4w
35436 HAYDVQQKExVUaGUgVVNFUlRSVVNUIE5ldHdvcmsxITAfBgNVBAsTGGh0dHA6Ly93d3cudXNlcnRy
35437 dXN0LmNvbTEbMBkGA1UEAxMSVVROIC0gREFUQUNvcnAgU0dDMIIBIjANBgkqhkiG9w0BAQEFAAOC
35438 AQ8AMIIBCgKCAQEA3+5YEKIrblXEjr8uRgnn4AgPLit6E5Qbvfa2gI5lBZMAHryv4g+OGQ0SR+ys
35439 raP6LnD43m77VkIVni5c7yPeIbkFdicZD0/Ww5y0vpQZY/KmEQrrU0icvvIpOxboGqBMpsn0GFlo
35440 wHDyUwDAXlCCpVZvNvlK4ESGoE1O1kduSUrLZ9emxAW5jh70/P/N5zbgnAVssjMiFdC04MwXwLLA
35441 9P4yPykqlXvY8qdOD1R8oQ2AswkDwf9c3V6aPryuvEeKaq5xyh+xKrhfQgUL7EYw0XILyulWbfXv
35442 33i+Ybqypa4ETLyorGkVl73v67SMvzX41MPRKA5cOp9wGDMgd8SirwIDAQABo4GrMIGoMAsGA1Ud
35443 DwQEAwIBxjAPBgNVHRMBAf8EBTADAQH/MB0GA1UdDgQWBBRTMtGzz3/64PGgXYVOktKeRR20TzA9
35444 BgNVHR8ENjA0MDKgMKAuhixodHRwOi8vY3JsLnVzZXJ0cnVzdC5jb20vVVROLURBVEFDb3JwU0dD
35445 LmNybDAqBgNVHSUEIzAhBggrBgEFBQcDAQYKKwYBBAGCNwoDAwYJYIZIAYb4QgQBMA0GCSqGSIb3
35446 DQEBBQUAA4IBAQAnNZcAiosovcYzMB4p/OL31ZjUQLtgyr+rFywJNn9Q+kHcrpY6CiM+iVnJowft
35447 Gzet/Hy+UUla3joKVAgWRcKZsYfNjGjgaQPpxE6YsjuMFrMOoAyYUJuTqXAJyCyjj98C5OBxOvG0
35448 I3KgqgHf35g+FFCgMSa9KOlaMCZ1+XtgHI3zzVAmbQQnmt/VDUVHKWss5nbZqSl9Mt3JNjy9rjXx
35449 EZ4du5A/EkdOjtd+D2JzHVImOBwYSf0wdJrE5SIv2MCN7ZF6TACPcn9d2t0bi0Vr591pl6jFVkwP
35450 DPafepE39peC4N1xaf92P2BNPM/3mfnGV/TJVTl4uix5yaaIK/QI
35451 -----END CERTIFICATE-----
35452
35453 UTN USERFirst Email Root CA
35454 ===========================
35455 -----BEGIN CERTIFICATE-----
35456 MIIEojCCA4qgAwIBAgIQRL4Mi1AAJLQR0zYlJWfJiTANBgkqhkiG9w0BAQUFADCBrjELMAkGA1UE
35457 BhMCVVMxCzAJBgNVBAgTAlVUMRcwFQYDVQQHEw5TYWx0IExha2UgQ2l0eTEeMBwGA1UEChMVVGhl
35458 IFVTRVJUUlVTVCBOZXR3b3JrMSEwHwYDVQQLExhodHRwOi8vd3d3LnVzZXJ0cnVzdC5jb20xNjA0
35459 BgNVBAMTLVVUTi1VU0VSRmlyc3QtQ2xpZW50IEF1dGhlbnRpY2F0aW9uIGFuZCBFbWFpbDAeFw05
35460 OTA3MDkxNzI4NTBaFw0xOTA3MDkxNzM2NThaMIGuMQswCQYDVQQGEwJVUzELMAkGA1UECBMCVVQx
35461 FzAVBgNVBAcTDlNhbHQgTGFrZSBDaXR5MR4wHAYDVQQKExVUaGUgVVNFUlRSVVNUIE5ldHdvcmsx
35462 ITAfBgNVBAsTGGh0dHA6Ly93d3cudXNlcnRydXN0LmNvbTE2MDQGA1UEAxMtVVROLVVTRVJGaXJz
35463 dC1DbGllbnQgQXV0aGVudGljYXRpb24gYW5kIEVtYWlsMIIBIjANBgkqhkiG9w0BAQEFAAOCAQ8A
35464 MIIBCgKCAQEAsjmFpPJ9q0E7YkY3rs3BYHW8OWX5ShpHornMSMxqmNVNNRm5pELlzkniii8efNIx
35465 B8dOtINknS4p1aJkxIW9hVE1eaROaJB7HHqkkqgX8pgV8pPMyaQylbsMTzC9mKALi+VuG6JG+ni8
35466 om+rWV6lL8/K2m2qL+usobNqqrcuZzWLeeEeaYji5kbNoKXqvgvOdjp6Dpvq/NonWz1zHyLmSGHG
35467 TPNpsaguG7bUMSAsvIKKjqQOpdeJQ/wWWq8dcdcRWdq6hw2v+vPhwvCkxWeM1tZUOt4KpLoDd7Nl
35468 yP0e03RiqhjKaJMeoYV+9Udly/hNVyh00jT/MLbu9mIwFIws6wIDAQABo4G5MIG2MAsGA1UdDwQE
35469 AwIBxjAPBgNVHRMBAf8EBTADAQH/MB0GA1UdDgQWBBSJgmd9xJ0mcABLtFBIfN49rgRufTBYBgNV
35470 HR8EUTBPME2gS6BJhkdodHRwOi8vY3JsLnVzZXJ0cnVzdC5jb20vVVROLVVTRVJGaXJzdC1DbGll
35471 bnRBdXRoZW50aWNhdGlvbmFuZEVtYWlsLmNybDAdBgNVHSUEFjAUBggrBgEFBQcDAgYIKwYBBQUH
35472 AwQwDQYJKoZIhvcNAQEFBQADggEBALFtYV2mGn98q0rkMPxTbyUkxsrt4jFcKw7u7mFVbwQ+zzne
35473 xRtJlOTrIEy05p5QLnLZjfWqo7NK2lYcYJeA3IKirUq9iiv/Cwm0xtcgBEXkzYABurorbs6q15L+
35474 5K/r9CYdFip/bDCVNy8zEqx/3cfREYxRmLLQo5HQrfafnoOTHh1CuEava2bwm3/q4wMC5QJRwarV
35475 NZ1yQAOJujEdxRBoUp7fooXFXAimeOZTT7Hot9MUnpOmw2TjrH5xzbyf6QMbzPvprDHBr3wVdAKZ
35476 w7JHpsIyYdfHb0gkUSeh1YdV8nuPmD0Wnu51tvjQjvLzxq4oW6fw8zYX/MMF08oDSlQ=
35477 -----END CERTIFICATE-----
35478
35479 UTN USERFirst Hardware Root CA
35480 ==============================
35481 -----BEGIN CERTIFICATE-----
35482 MIIEdDCCA1ygAwIBAgIQRL4Mi1AAJLQR0zYq/mUK/TANBgkqhkiG9w0BAQUFADCBlzELMAkGA1UE
35483 BhMCVVMxCzAJBgNVBAgTAlVUMRcwFQYDVQQHEw5TYWx0IExha2UgQ2l0eTEeMBwGA1UEChMVVGhl
35484 IFVTRVJUUlVTVCBOZXR3b3JrMSEwHwYDVQQLExhodHRwOi8vd3d3LnVzZXJ0cnVzdC5jb20xHzAd
35485 BgNVBAMTFlVUTi1VU0VSRmlyc3QtSGFyZHdhcmUwHhcNOTkwNzA5MTgxMDQyWhcNMTkwNzA5MTgx
35486 OTIyWjCBlzELMAkGA1UEBhMCVVMxCzAJBgNVBAgTAlVUMRcwFQYDVQQHEw5TYWx0IExha2UgQ2l0
35487 eTEeMBwGA1UEChMVVGhlIFVTRVJUUlVTVCBOZXR3b3JrMSEwHwYDVQQLExhodHRwOi8vd3d3LnVz
35488 ZXJ0cnVzdC5jb20xHzAdBgNVBAMTFlVUTi1VU0VSRmlyc3QtSGFyZHdhcmUwggEiMA0GCSqGSIb3
35489 DQEBAQUAA4IBDwAwggEKAoIBAQCx98M4P7Sof885glFn0G2f0v9Y8+efK+wNiVSZuTiZFvfgIXlI
35490 wrthdBKWHTxqctU8EGc6Oe0rE81m65UJM6Rsl7HoxuzBdXmcRl6Nq9Bq/bkqVRcQVLMZ8Jr28bFd
35491 tqdt++BxF2uiiPsA3/4aMXcMmgF6sTLjKwEHOG7DpV4jvEWbe1DByTCP2+UretNb+zNAHqDVmBe8
35492 i4fDidNdoI6yqqr2jmmIBsX6iSHzCJ1pLgkzmykNRg+MzEk0sGlRvfkGzWitZky8PqxhvQqIDsjf
35493 Pe58BEydCl5rkdbux+0ojatNh4lz0G6k0B4WixThdkQDf2Os5M1JnMWS9KsyoUhbAgMBAAGjgbkw
35494 gbYwCwYDVR0PBAQDAgHGMA8GA1UdEwEB/wQFMAMBAf8wHQYDVR0OBBYEFKFyXyYbKJhDlV0HN9WF
35495 lp1L0sNFMEQGA1UdHwQ9MDswOaA3oDWGM2h0dHA6Ly9jcmwudXNlcnRydXN0LmNvbS9VVE4tVVNF
35496 UkZpcnN0LUhhcmR3YXJlLmNybDAxBgNVHSUEKjAoBggrBgEFBQcDAQYIKwYBBQUHAwUGCCsGAQUF
35497 BwMGBggrBgEFBQcDBzANBgkqhkiG9w0BAQUFAAOCAQEARxkP3nTGmZev/K0oXnWO6y1n7k57K9cM
35498 //bey1WiCuFMVGWTYGufEpytXoMs61quwOQt9ABjHbjAbPLPSbtNk28GpgoiskliCE7/yMgUsogW
35499 XecB5BKV5UU0s4tpvc+0hY91UZ59Ojg6FEgSxvunOxqNDYJAB+gECJChicsZUN/KHAG8HQQZexB2
35500 lzvukJDKxA4fFm517zP4029bHpbj4HR3dHuKom4t3XbWOTCC8KucUvIqx69JXn7HaOWCgchqJ/kn
35501 iCrVWFCVH/A7HFe7fRQ5YiuayZSSKqMiDP+JJn1fIytH1xUdqWqeUQ0qUZ6B+dQ7XnASfxAynB67
35502 nfhmqA==
35503 -----END CERTIFICATE-----
35504
35505 UTN USERFirst Object Root CA
35506 ============================
35507 -----BEGIN CERTIFICATE-----
35508 MIIEZjCCA06gAwIBAgIQRL4Mi1AAJLQR0zYt4LNfGzANBgkqhkiG9w0BAQUFADCBlTELMAkGA1UE
35509 BhMCVVMxCzAJBgNVBAgTAlVUMRcwFQYDVQQHEw5TYWx0IExha2UgQ2l0eTEeMBwGA1UEChMVVGhl
35510 IFVTRVJUUlVTVCBOZXR3b3JrMSEwHwYDVQQLExhodHRwOi8vd3d3LnVzZXJ0cnVzdC5jb20xHTAb
35511 BgNVBAMTFFVUTi1VU0VSRmlyc3QtT2JqZWN0MB4XDTk5MDcwOTE4MzEyMFoXDTE5MDcwOTE4NDAz
35512 NlowgZUxCzAJBgNVBAYTAlVTMQswCQYDVQQIEwJVVDEXMBUGA1UEBxMOU2FsdCBMYWtlIENpdHkx
35513 HjAcBgNVBAoTFVRoZSBVU0VSVFJVU1QgTmV0d29yazEhMB8GA1UECxMYaHR0cDovL3d3dy51c2Vy
35514 dHJ1c3QuY29tMR0wGwYDVQQDExRVVE4tVVNFUkZpcnN0LU9iamVjdDCCASIwDQYJKoZIhvcNAQEB
35515 BQADggEPADCCAQoCggEBAM6qgT+jo2F4qjEAVZURnicPHxzfOpuCaDDASmEd8S8O+r5596Uj71VR
35516 loTN2+O5bj4x2AogZ8f02b+U60cEPgLOKqJdhwQJ9jCdGIqXsqoc/EHSoTbL+z2RuufZcDX65OeQ
35517 w5ujm9M89RKZd7G3CeBo5hy485RjiGpq/gt2yb70IuRnuasaXnfBhQfdDWy/7gbHd2pBnqcP1/vu
35518 lBe3/IW+pKvEHDHd17bR5PDv3xaPslKT16HUiaEHLr/hARJCHhrh2JU022R5KP+6LhHC5ehbkkj7
35519 RwvCbNqtMoNB86XlQXD9ZZBt+vpRxPm9lisZBCzTbafc8H9vg2XiaquHhnUCAwEAAaOBrzCBrDAL
35520 BgNVHQ8EBAMCAcYwDwYDVR0TAQH/BAUwAwEB/zAdBgNVHQ4EFgQU2u1kdBScFDyr3ZmpvVsoTYs8
35521 ydgwQgYDVR0fBDswOTA3oDWgM4YxaHR0cDovL2NybC51c2VydHJ1c3QuY29tL1VUTi1VU0VSRmly
35522 c3QtT2JqZWN0LmNybDApBgNVHSUEIjAgBggrBgEFBQcDAwYIKwYBBQUHAwgGCisGAQQBgjcKAwQw
35523 DQYJKoZIhvcNAQEFBQADggEBAAgfUrE3RHjb/c652pWWmKpVZIC1WkDdIaXFwfNfLEzIR1pp6ujw
35524 NTX00CXzyKakh0q9G7FzCL3Uw8q2NbtZhncxzaeAFK4T7/yxSPlrJSUtUbYsbUXBmMiKVl0+7kNO
35525 PmsnjtA6S4ULX9Ptaqd1y9Fahy85dRNacrACgZ++8A+EVCBibGnU4U3GDZlDAQ0Slox4nb9QorFE
35526 qmrPF3rPbw/U+CRVX/A0FklmPlBGyWNxODFiuGK581OtbLUrohKqGU8J2l7nk8aOFAj+8DCAGKCG
35527 hU3IfdeLA/5u1fedFqySLKAj5ZyRUh+U3xeUc8OzwcFxBSAAeL0TUh2oPs0AH8g=
35528 -----END CERTIFICATE-----
35529
35530 Camerfirma Chambers of Commerce Root
35531 ====================================
35532 -----BEGIN CERTIFICATE-----
35533 MIIEvTCCA6WgAwIBAgIBADANBgkqhkiG9w0BAQUFADB/MQswCQYDVQQGEwJFVTEnMCUGA1UEChMe
35534 QUMgQ2FtZXJmaXJtYSBTQSBDSUYgQTgyNzQzMjg3MSMwIQYDVQQLExpodHRwOi8vd3d3LmNoYW1i
35535 ZXJzaWduLm9yZzEiMCAGA1UEAxMZQ2hhbWJlcnMgb2YgQ29tbWVyY2UgUm9vdDAeFw0wMzA5MzAx
35536 NjEzNDNaFw0zNzA5MzAxNjEzNDRaMH8xCzAJBgNVBAYTAkVVMScwJQYDVQQKEx5BQyBDYW1lcmZp
35537 cm1hIFNBIENJRiBBODI3NDMyODcxIzAhBgNVBAsTGmh0dHA6Ly93d3cuY2hhbWJlcnNpZ24ub3Jn
35538 MSIwIAYDVQQDExlDaGFtYmVycyBvZiBDb21tZXJjZSBSb290MIIBIDANBgkqhkiG9w0BAQEFAAOC
35539 AQ0AMIIBCAKCAQEAtzZV5aVdGDDg2olUkfzIx1L4L1DZ77F1c2VHfRtbunXF/KGIJPov7coISjlU
35540 xFF6tdpg6jg8gbLL8bvZkSM/SAFwdakFKq0fcfPJVD0dBmpAPrMMhe5cG3nCYsS4No41XQEMIwRH
35541 NaqbYE6gZj3LJgqcQKH0XZi/caulAGgq7YN6D6IUtdQis4CwPAxaUWktWBiP7Zme8a7ileb2R6jW
35542 DA+wWFjbw2Y3npuRVDM30pQcakjJyfKl2qUMI/cjDpwyVV5xnIQFUZot/eZOKjRa3spAN2cMVCFV
35543 d9oKDMyXroDclDZK9D7ONhMeU+SsTjoF7Nuucpw4i9A5O4kKPnf+dQIBA6OCAUQwggFAMBIGA1Ud
35544 EwEB/wQIMAYBAf8CAQwwPAYDVR0fBDUwMzAxoC+gLYYraHR0cDovL2NybC5jaGFtYmVyc2lnbi5v
35545 cmcvY2hhbWJlcnNyb290LmNybDAdBgNVHQ4EFgQU45T1sU3p26EpW1eLTXYGduHRooowDgYDVR0P
35546 AQH/BAQDAgEGMBEGCWCGSAGG+EIBAQQEAwIABzAnBgNVHREEIDAegRxjaGFtYmVyc3Jvb3RAY2hh
35547 bWJlcnNpZ24ub3JnMCcGA1UdEgQgMB6BHGNoYW1iZXJzcm9vdEBjaGFtYmVyc2lnbi5vcmcwWAYD
35548 VR0gBFEwTzBNBgsrBgEEAYGHLgoDATA+MDwGCCsGAQUFBwIBFjBodHRwOi8vY3BzLmNoYW1iZXJz
35549 aWduLm9yZy9jcHMvY2hhbWJlcnNyb290Lmh0bWwwDQYJKoZIhvcNAQEFBQADggEBAAxBl8IahsAi
35550 fJ/7kPMa0QOx7xP5IV8EnNrJpY0nbJaHkb5BkAFyk+cefV/2icZdp0AJPaxJRUXcLo0waLIJuvvD
35551 L8y6C98/d3tGfToSJI6WjzwFCm/SlCgdbQzALogi1djPHRPH8EjX1wWnz8dHnjs8NMiAT9QUu/wN
35552 UPf6s+xCX6ndbcj0dc97wXImsQEcXCz9ek60AcUFV7nnPKoF2YjpB0ZBzu9Bga5Y34OirsrXdx/n
35553 ADydb47kMgkdTXg0eDQ8lJsm7U9xxhl6vSAiSFr+S30Dt+dYvsYyTnQeaN2oaFuzPu5ifdmA6Ap1
35554 erfutGWaIZDgqtCYvDi1czyL+Nw=
35555 -----END CERTIFICATE-----
35556
35557 Camerfirma Global Chambersign Root
35558 ==================================
35559 -----BEGIN CERTIFICATE-----
35560 MIIExTCCA62gAwIBAgIBADANBgkqhkiG9w0BAQUFADB9MQswCQYDVQQGEwJFVTEnMCUGA1UEChMe
35561 QUMgQ2FtZXJmaXJtYSBTQSBDSUYgQTgyNzQzMjg3MSMwIQYDVQQLExpodHRwOi8vd3d3LmNoYW1i
35562 ZXJzaWduLm9yZzEgMB4GA1UEAxMXR2xvYmFsIENoYW1iZXJzaWduIFJvb3QwHhcNMDMwOTMwMTYx
35563 NDE4WhcNMzcwOTMwMTYxNDE4WjB9MQswCQYDVQQGEwJFVTEnMCUGA1UEChMeQUMgQ2FtZXJmaXJt
35564 YSBTQSBDSUYgQTgyNzQzMjg3MSMwIQYDVQQLExpodHRwOi8vd3d3LmNoYW1iZXJzaWduLm9yZzEg
35565 MB4GA1UEAxMXR2xvYmFsIENoYW1iZXJzaWduIFJvb3QwggEgMA0GCSqGSIb3DQEBAQUAA4IBDQAw
35566 ggEIAoIBAQCicKLQn0KuWxfH2H3PFIP8T8mhtxOviteePgQKkotgVvq0Mi+ITaFgCPS3CU6gSS9J
35567 1tPfnZdan5QEcOw/Wdm3zGaLmFIoCQLfxS+EjXqXd7/sQJ0lcqu1PzKY+7e3/HKE5TWH+VX6ox8O
35568 by4o3Wmg2UIQxvi1RMLQQ3/bvOSiPGpVeAp3qdjqGTK3L/5cPxvusZjsyq16aUXjlg9V9ubtdepl
35569 6DJWk0aJqCWKZQbua795B9Dxt6/tLE2Su8CoX6dnfQTyFQhwrJLWfQTSM/tMtgsL+xrJxI0DqX5c
35570 8lCrEqWhz0hQpe/SyBoT+rB/sYIcd2oPX9wLlY/vQ37mRQklAgEDo4IBUDCCAUwwEgYDVR0TAQH/
35571 BAgwBgEB/wIBDDA/BgNVHR8EODA2MDSgMqAwhi5odHRwOi8vY3JsLmNoYW1iZXJzaWduLm9yZy9j
35572 aGFtYmVyc2lnbnJvb3QuY3JsMB0GA1UdDgQWBBRDnDafsJ4wTcbOX60Qq+UDpfqpFDAOBgNVHQ8B
35573 Af8EBAMCAQYwEQYJYIZIAYb4QgEBBAQDAgAHMCoGA1UdEQQjMCGBH2NoYW1iZXJzaWducm9vdEBj
35574 aGFtYmVyc2lnbi5vcmcwKgYDVR0SBCMwIYEfY2hhbWJlcnNpZ25yb290QGNoYW1iZXJzaWduLm9y
35575 ZzBbBgNVHSAEVDBSMFAGCysGAQQBgYcuCgEBMEEwPwYIKwYBBQUHAgEWM2h0dHA6Ly9jcHMuY2hh
35576 bWJlcnNpZ24ub3JnL2Nwcy9jaGFtYmVyc2lnbnJvb3QuaHRtbDANBgkqhkiG9w0BAQUFAAOCAQEA
35577 PDtwkfkEVCeR4e3t/mh/YV3lQWVPMvEYBZRqHN4fcNs+ezICNLUMbKGKfKX0j//U2K0X1S0E0T9Y
35578 gOKBWYi+wONGkyT+kL0mojAt6JcmVzWJdJYY9hXiryQZVgICsroPFOrGimbBhkVVi76SvpykBMdJ
35579 PJ7oKXqJ1/6v/2j1pReQvayZzKWGVwlnRtvWFsJG8eSpUPWP0ZIV018+xgBJOm5YstHRJw0lyDL4
35580 IBHNfTIzSJRUTN3cecQwn+uOuFW114hcxWokPbLTBQNRxgfvzBRydD1ucs4YKIxKoHflCStFREes
35581 t2d/AYoFWpO+ocH/+OcOZ6RHSXZddZAa9SaP8A==
35582 -----END CERTIFICATE-----
35583
35584 NetLock Qualified (Class QA) Root
35585 =================================
35586 -----BEGIN CERTIFICATE-----
35587 MIIG0TCCBbmgAwIBAgIBezANBgkqhkiG9w0BAQUFADCByTELMAkGA1UEBhMCSFUxETAPBgNVBAcT
35588 CEJ1ZGFwZXN0MScwJQYDVQQKEx5OZXRMb2NrIEhhbG96YXRiaXp0b25zYWdpIEtmdC4xGjAYBgNV
35589 BAsTEVRhbnVzaXR2YW55a2lhZG9rMUIwQAYDVQQDEzlOZXRMb2NrIE1pbm9zaXRldHQgS296amVn
35590 eXpvaSAoQ2xhc3MgUUEpIFRhbnVzaXR2YW55a2lhZG8xHjAcBgkqhkiG9w0BCQEWD2luZm9AbmV0
35591 bG9jay5odTAeFw0wMzAzMzAwMTQ3MTFaFw0yMjEyMTUwMTQ3MTFaMIHJMQswCQYDVQQGEwJIVTER
35592 MA8GA1UEBxMIQnVkYXBlc3QxJzAlBgNVBAoTHk5ldExvY2sgSGFsb3phdGJpenRvbnNhZ2kgS2Z0
35593 LjEaMBgGA1UECxMRVGFudXNpdHZhbnlraWFkb2sxQjBABgNVBAMTOU5ldExvY2sgTWlub3NpdGV0
35594 dCBLb3pqZWd5em9pIChDbGFzcyBRQSkgVGFudXNpdHZhbnlraWFkbzEeMBwGCSqGSIb3DQEJARYP
35595 aW5mb0BuZXRsb2NrLmh1MIIBIjANBgkqhkiG9w0BAQEFAAOCAQ8AMIIBCgKCAQEAx1Ilstg91IRV
35596 CacbvWy5FPSKAtt2/GoqeKvld/Bu4IwjZ9ulZJm53QE+b+8tmjwi8F3JV6BVQX/yQ15YglMxZc4e
35597 8ia6AFQer7C8HORSjKAyr7c3sVNnaHRnUPYtLmTeriZ539+Zhqurf4XsoPuAzPS4DB6TRWO53Lhb
35598 m+1bOdRfYrCnjnxmOCyqsQhjF2d9zL2z8cM/z1A57dEZgxXbhxInlrfa6uWdvLrqOU+L73Sa58XQ
35599 0uqGURzk/mQIKAR5BevKxXEOC++r6uwSEaEYBTJp0QwsGj0lmT+1fMptsK6ZmfoIYOcZwvK9UdPM
35600 0wKswREMgM6r3JSda6M5UzrWhQIDAMV9o4ICwDCCArwwEgYDVR0TAQH/BAgwBgEB/wIBBDAOBgNV
35601 HQ8BAf8EBAMCAQYwggJ1BglghkgBhvhCAQ0EggJmFoICYkZJR1lFTEVNISBFemVuIHRhbnVzaXR2
35602 YW55IGEgTmV0TG9jayBLZnQuIE1pbm9zaXRldHQgU3pvbGdhbHRhdGFzaSBTemFiYWx5emF0YWJh
35603 biBsZWlydCBlbGphcmFzb2sgYWxhcGphbiBrZXN6dWx0LiBBIG1pbm9zaXRldHQgZWxla3Ryb25p
35604 a3VzIGFsYWlyYXMgam9naGF0YXMgZXJ2ZW55ZXN1bGVzZW5laywgdmFsYW1pbnQgZWxmb2dhZGFz
35605 YW5hayBmZWx0ZXRlbGUgYSBNaW5vc2l0ZXR0IFN6b2xnYWx0YXRhc2kgU3phYmFseXphdGJhbiwg
35606 YXogQWx0YWxhbm9zIFN6ZXJ6b2Rlc2kgRmVsdGV0ZWxla2JlbiBlbG9pcnQgZWxsZW5vcnplc2kg
35607 ZWxqYXJhcyBtZWd0ZXRlbGUuIEEgZG9rdW1lbnR1bW9rIG1lZ3RhbGFsaGF0b2sgYSBodHRwczov
35608 L3d3dy5uZXRsb2NrLmh1L2RvY3MvIGNpbWVuIHZhZ3kga2VyaGV0b2sgYXogaW5mb0BuZXRsb2Nr
35609 Lm5ldCBlLW1haWwgY2ltZW4uIFdBUk5JTkchIFRoZSBpc3N1YW5jZSBhbmQgdGhlIHVzZSBvZiB0
35610 aGlzIGNlcnRpZmljYXRlIGFyZSBzdWJqZWN0IHRvIHRoZSBOZXRMb2NrIFF1YWxpZmllZCBDUFMg
35611 YXZhaWxhYmxlIGF0IGh0dHBzOi8vd3d3Lm5ldGxvY2suaHUvZG9jcy8gb3IgYnkgZS1tYWlsIGF0
35612 IGluZm9AbmV0bG9jay5uZXQwHQYDVR0OBBYEFAlqYhaSsFq7VQ7LdTI6MuWyIckoMA0GCSqGSIb3
35613 DQEBBQUAA4IBAQCRalCc23iBmz+LQuM7/KbD7kPgz/PigDVJRXYC4uMvBcXxKufAQTPGtpvQMznN
35614 wNuhrWw3AkxYQTvyl5LGSKjN5Yo5iWH5Upfpvfb5lHTocQ68d4bDBsxafEp+NFAwLvt/MpqNPfMg
35615 W/hqyobzMUwsWYACff44yTB1HLdV47yfuqhthCgFdbOLDcCRVCHnpgu0mfVRQdzNo0ci2ccBgcTc
35616 R08m6h/t280NmPSjnLRzMkqWmf68f8glWPhY83ZmiVSkpj7EUFy6iRiCdUgh0k8T6GB+B3bbELVR
35617 5qq5aKrN9p2QdRLqOBrKROi3macqaJVmlaut74nLYKkGEsaUR+ko
35618 -----END CERTIFICATE-----
35619
35620 NetLock Notary (Class A) Root
35621 =============================
35622 -----BEGIN CERTIFICATE-----
35623 MIIGfTCCBWWgAwIBAgICAQMwDQYJKoZIhvcNAQEEBQAwga8xCzAJBgNVBAYTAkhVMRAwDgYDVQQI
35624 EwdIdW5nYXJ5MREwDwYDVQQHEwhCdWRhcGVzdDEnMCUGA1UEChMeTmV0TG9jayBIYWxvemF0Yml6
35625 dG9uc2FnaSBLZnQuMRowGAYDVQQLExFUYW51c2l0dmFueWtpYWRvazE2MDQGA1UEAxMtTmV0TG9j
35626 ayBLb3pqZWd5em9pIChDbGFzcyBBKSBUYW51c2l0dmFueWtpYWRvMB4XDTk5MDIyNDIzMTQ0N1oX
35627 DTE5MDIxOTIzMTQ0N1owga8xCzAJBgNVBAYTAkhVMRAwDgYDVQQIEwdIdW5nYXJ5MREwDwYDVQQH
35628 EwhCdWRhcGVzdDEnMCUGA1UEChMeTmV0TG9jayBIYWxvemF0Yml6dG9uc2FnaSBLZnQuMRowGAYD
35629 VQQLExFUYW51c2l0dmFueWtpYWRvazE2MDQGA1UEAxMtTmV0TG9jayBLb3pqZWd5em9pIChDbGFz
35630 cyBBKSBUYW51c2l0dmFueWtpYWRvMIIBIjANBgkqhkiG9w0BAQEFAAOCAQ8AMIIBCgKCAQEAvHSM
35631 D7tM9DceqQWC2ObhbHDqeLVu0ThEDaiDzl3S1tWBxdRL51uUcCbbO51qTGL3cfNk1mE7PetzozfZ
35632 z+qMkjvN9wfcZnSX9EUi3fRc4L9t875lM+QVOr/bmJBVOMTtplVjC7B4BPTjbsE/jvxReB+SnoPC
35633 /tmwqcm8WgD/qaiYdPv2LD4VOQ22BFWoDpggQrOxJa1+mm9dU7GrDPzr4PN6s6iz/0b2Y6LYOph7
35634 tqyF/7AlT3Rj5xMHpQqPBffAZG9+pyeAlt7ULoZgx2srXnN7F+eRP2QM2EsiNCubMvJIH5+hCoR6
35635 4sKtlz2O1cH5VqNQ6ca0+pii7pXmKgOM3wIDAQABo4ICnzCCApswDgYDVR0PAQH/BAQDAgAGMBIG
35636 A1UdEwEB/wQIMAYBAf8CAQQwEQYJYIZIAYb4QgEBBAQDAgAHMIICYAYJYIZIAYb4QgENBIICURaC
35637 Ak1GSUdZRUxFTSEgRXplbiB0YW51c2l0dmFueSBhIE5ldExvY2sgS2Z0LiBBbHRhbGFub3MgU3pv
35638 bGdhbHRhdGFzaSBGZWx0ZXRlbGVpYmVuIGxlaXJ0IGVsamFyYXNvayBhbGFwamFuIGtlc3p1bHQu
35639 IEEgaGl0ZWxlc2l0ZXMgZm9seWFtYXRhdCBhIE5ldExvY2sgS2Z0LiB0ZXJtZWtmZWxlbG9zc2Vn
35640 LWJpenRvc2l0YXNhIHZlZGkuIEEgZGlnaXRhbGlzIGFsYWlyYXMgZWxmb2dhZGFzYW5hayBmZWx0
35641 ZXRlbGUgYXogZWxvaXJ0IGVsbGVub3J6ZXNpIGVsamFyYXMgbWVndGV0ZWxlLiBBeiBlbGphcmFz
35642 IGxlaXJhc2EgbWVndGFsYWxoYXRvIGEgTmV0TG9jayBLZnQuIEludGVybmV0IGhvbmxhcGphbiBh
35643 IGh0dHBzOi8vd3d3Lm5ldGxvY2submV0L2RvY3MgY2ltZW4gdmFneSBrZXJoZXRvIGF6IGVsbGVu
35644 b3J6ZXNAbmV0bG9jay5uZXQgZS1tYWlsIGNpbWVuLiBJTVBPUlRBTlQhIFRoZSBpc3N1YW5jZSBh
35645 bmQgdGhlIHVzZSBvZiB0aGlzIGNlcnRpZmljYXRlIGlzIHN1YmplY3QgdG8gdGhlIE5ldExvY2sg
35646 Q1BTIGF2YWlsYWJsZSBhdCBodHRwczovL3d3dy5uZXRsb2NrLm5ldC9kb2NzIG9yIGJ5IGUtbWFp
35647 bCBhdCBjcHNAbmV0bG9jay5uZXQuMA0GCSqGSIb3DQEBBAUAA4IBAQBIJEb3ulZv+sgoA0BO5TE5
35648 ayZrU3/b39/zcT0mwBQOxmd7I6gMc90Bu8bKbjc5VdXHjFYgDigKDtIqpLBJUsY4B/6+CgmM0ZjP
35649 ytoUMaFP0jn8DxEsQ8Pdq5PHVT5HfBgaANzze9jyf1JsIPQLX2lS9O74silg6+NJMSEN1rUQQeJB
35650 CWziGppWS3cC9qCbmieH6FUpccKQn0V4GuEVZD3QDtigdp+uxdAu6tYPVuxkf1qbFFgBJ34TUMdr
35651 KuZoPL9coAob4Q566eKAw+np9v1sEZ7Q5SgnK1QyQhSCdeZK8CtmdWOMovsEPoMOmzbwGOQmIMOM
35652 8CgHrTwXZoi1/baI
35653 -----END CERTIFICATE-----
35654
35655 NetLock Business (Class B) Root
35656 ===============================
35657 -----BEGIN CERTIFICATE-----
35658 MIIFSzCCBLSgAwIBAgIBaTANBgkqhkiG9w0BAQQFADCBmTELMAkGA1UEBhMCSFUxETAPBgNVBAcT
35659 CEJ1ZGFwZXN0MScwJQYDVQQKEx5OZXRMb2NrIEhhbG96YXRiaXp0b25zYWdpIEtmdC4xGjAYBgNV
35660 BAsTEVRhbnVzaXR2YW55a2lhZG9rMTIwMAYDVQQDEylOZXRMb2NrIFV6bGV0aSAoQ2xhc3MgQikg
35661 VGFudXNpdHZhbnlraWFkbzAeFw05OTAyMjUxNDEwMjJaFw0xOTAyMjAxNDEwMjJaMIGZMQswCQYD
35662 VQQGEwJIVTERMA8GA1UEBxMIQnVkYXBlc3QxJzAlBgNVBAoTHk5ldExvY2sgSGFsb3phdGJpenRv
35663 bnNhZ2kgS2Z0LjEaMBgGA1UECxMRVGFudXNpdHZhbnlraWFkb2sxMjAwBgNVBAMTKU5ldExvY2sg
35664 VXpsZXRpIChDbGFzcyBCKSBUYW51c2l0dmFueWtpYWRvMIGfMA0GCSqGSIb3DQEBAQUAA4GNADCB
35665 iQKBgQCx6gTsIKAjwo84YM/HRrPVG/77uZmeBNwcf4xKgZjupNTKihe5In+DCnVMm8Bp2GQ5o+2S
35666 o/1bXHQawEfKOml2mrriRBf8TKPV/riXiK+IA4kfpPIEPsgHC+b5sy96YhQJRhTKZPWLgLViqNhr
35667 1nGTLbO/CVRY7QbrqHvcQ7GhaQIDAQABo4ICnzCCApswEgYDVR0TAQH/BAgwBgEB/wIBBDAOBgNV
35668 HQ8BAf8EBAMCAAYwEQYJYIZIAYb4QgEBBAQDAgAHMIICYAYJYIZIAYb4QgENBIICURaCAk1GSUdZ
35669 RUxFTSEgRXplbiB0YW51c2l0dmFueSBhIE5ldExvY2sgS2Z0LiBBbHRhbGFub3MgU3pvbGdhbHRh
35670 dGFzaSBGZWx0ZXRlbGVpYmVuIGxlaXJ0IGVsamFyYXNvayBhbGFwamFuIGtlc3p1bHQuIEEgaGl0
35671 ZWxlc2l0ZXMgZm9seWFtYXRhdCBhIE5ldExvY2sgS2Z0LiB0ZXJtZWtmZWxlbG9zc2VnLWJpenRv
35672 c2l0YXNhIHZlZGkuIEEgZGlnaXRhbGlzIGFsYWlyYXMgZWxmb2dhZGFzYW5hayBmZWx0ZXRlbGUg
35673 YXogZWxvaXJ0IGVsbGVub3J6ZXNpIGVsamFyYXMgbWVndGV0ZWxlLiBBeiBlbGphcmFzIGxlaXJh
35674 c2EgbWVndGFsYWxoYXRvIGEgTmV0TG9jayBLZnQuIEludGVybmV0IGhvbmxhcGphbiBhIGh0dHBz
35675 Oi8vd3d3Lm5ldGxvY2submV0L2RvY3MgY2ltZW4gdmFneSBrZXJoZXRvIGF6IGVsbGVub3J6ZXNA
35676 bmV0bG9jay5uZXQgZS1tYWlsIGNpbWVuLiBJTVBPUlRBTlQhIFRoZSBpc3N1YW5jZSBhbmQgdGhl
35677 IHVzZSBvZiB0aGlzIGNlcnRpZmljYXRlIGlzIHN1YmplY3QgdG8gdGhlIE5ldExvY2sgQ1BTIGF2
35678 YWlsYWJsZSBhdCBodHRwczovL3d3dy5uZXRsb2NrLm5ldC9kb2NzIG9yIGJ5IGUtbWFpbCBhdCBj
35679 cHNAbmV0bG9jay5uZXQuMA0GCSqGSIb3DQEBBAUAA4GBAATbrowXr/gOkDFOzT4JwG06sPgzTEdM
35680 43WIEJessDgVkcYplswhwG08pXTP2IKlOcNl40JwuyKQ433bNXbhoLXan3BukxowOR0w2y7jfLKR
35681 stE3Kfq51hdcR0/jHTjrn9V7lagonhVK0dHQKwCXoOKSNitjrFgBazMpUIaD8QFI
35682 -----END CERTIFICATE-----
35683
35684 NetLock Express (Class C) Root
35685 ==============================
35686 -----BEGIN CERTIFICATE-----
35687 MIIFTzCCBLigAwIBAgIBaDANBgkqhkiG9w0BAQQFADCBmzELMAkGA1UEBhMCSFUxETAPBgNVBAcT
35688 CEJ1ZGFwZXN0MScwJQYDVQQKEx5OZXRMb2NrIEhhbG96YXRiaXp0b25zYWdpIEtmdC4xGjAYBgNV
35689 BAsTEVRhbnVzaXR2YW55a2lhZG9rMTQwMgYDVQQDEytOZXRMb2NrIEV4cHJlc3N6IChDbGFzcyBD
35690 KSBUYW51c2l0dmFueWtpYWRvMB4XDTk5MDIyNTE0MDgxMVoXDTE5MDIyMDE0MDgxMVowgZsxCzAJ
35691 BgNVBAYTAkhVMREwDwYDVQQHEwhCdWRhcGVzdDEnMCUGA1UEChMeTmV0TG9jayBIYWxvemF0Yml6
35692 dG9uc2FnaSBLZnQuMRowGAYDVQQLExFUYW51c2l0dmFueWtpYWRvazE0MDIGA1UEAxMrTmV0TG9j
35693 ayBFeHByZXNzeiAoQ2xhc3MgQykgVGFudXNpdHZhbnlraWFkbzCBnzANBgkqhkiG9w0BAQEFAAOB
35694 jQAwgYkCgYEA6+ywbGGKIyWvYCDj2Z/8kwvbXY2wobNAOoLO/XXgeDIDhlqGlZHtU/qdQPzm6N3Z
35695 W3oDvV3zOwzDUXmbrVWg6dADEK8KuhRC2VImESLH0iDMgqSaqf64gXadarfSNnU+sYYJ9m5tfk63
35696 euyucYT2BDMIJTLrdKwWRMbkQJMdf60CAwEAAaOCAp8wggKbMBIGA1UdEwEB/wQIMAYBAf8CAQQw
35697 DgYDVR0PAQH/BAQDAgAGMBEGCWCGSAGG+EIBAQQEAwIABzCCAmAGCWCGSAGG+EIBDQSCAlEWggJN
35698 RklHWUVMRU0hIEV6ZW4gdGFudXNpdHZhbnkgYSBOZXRMb2NrIEtmdC4gQWx0YWxhbm9zIFN6b2xn
35699 YWx0YXRhc2kgRmVsdGV0ZWxlaWJlbiBsZWlydCBlbGphcmFzb2sgYWxhcGphbiBrZXN6dWx0LiBB
35700 IGhpdGVsZXNpdGVzIGZvbHlhbWF0YXQgYSBOZXRMb2NrIEtmdC4gdGVybWVrZmVsZWxvc3NlZy1i
35701 aXp0b3NpdGFzYSB2ZWRpLiBBIGRpZ2l0YWxpcyBhbGFpcmFzIGVsZm9nYWRhc2FuYWsgZmVsdGV0
35702 ZWxlIGF6IGVsb2lydCBlbGxlbm9yemVzaSBlbGphcmFzIG1lZ3RldGVsZS4gQXogZWxqYXJhcyBs
35703 ZWlyYXNhIG1lZ3RhbGFsaGF0byBhIE5ldExvY2sgS2Z0LiBJbnRlcm5ldCBob25sYXBqYW4gYSBo
35704 dHRwczovL3d3dy5uZXRsb2NrLm5ldC9kb2NzIGNpbWVuIHZhZ3kga2VyaGV0byBheiBlbGxlbm9y
35705 emVzQG5ldGxvY2submV0IGUtbWFpbCBjaW1lbi4gSU1QT1JUQU5UISBUaGUgaXNzdWFuY2UgYW5k
35706 IHRoZSB1c2Ugb2YgdGhpcyBjZXJ0aWZpY2F0ZSBpcyBzdWJqZWN0IHRvIHRoZSBOZXRMb2NrIENQ
35707 UyBhdmFpbGFibGUgYXQgaHR0cHM6Ly93d3cubmV0bG9jay5uZXQvZG9jcyBvciBieSBlLW1haWwg
35708 YXQgY3BzQG5ldGxvY2submV0LjANBgkqhkiG9w0BAQQFAAOBgQAQrX/XDDKACtiG8XmYta3UzbM2
35709 xJZIwVzNmtkFLp++UOv0JhQQLdRmF/iewSf98e3ke0ugbLWrmldwpu2gpO0u9f38vf5NNwgMvOOW
35710 gyL1SRt/Syu0VMGAfJlOHdCM7tCs5ZL6dVb+ZKATj7i4Fp1hBWeAyNDYpQcCNJgEjTME1A==
35711 -----END CERTIFICATE-----
35712
35713 XRamp Global CA Root
35714 ====================
35715 -----BEGIN CERTIFICATE-----
35716 MIIEMDCCAxigAwIBAgIQUJRs7Bjq1ZxN1ZfvdY+grTANBgkqhkiG9w0BAQUFADCBgjELMAkGA1UE
35717 BhMCVVMxHjAcBgNVBAsTFXd3dy54cmFtcHNlY3VyaXR5LmNvbTEkMCIGA1UEChMbWFJhbXAgU2Vj
35718 dXJpdHkgU2VydmljZXMgSW5jMS0wKwYDVQQDEyRYUmFtcCBHbG9iYWwgQ2VydGlmaWNhdGlvbiBB
35719 dXRob3JpdHkwHhcNMDQxMTAxMTcxNDA0WhcNMzUwMTAxMDUzNzE5WjCBgjELMAkGA1UEBhMCVVMx
35720 HjAcBgNVBAsTFXd3dy54cmFtcHNlY3VyaXR5LmNvbTEkMCIGA1UEChMbWFJhbXAgU2VjdXJpdHkg
35721 U2VydmljZXMgSW5jMS0wKwYDVQQDEyRYUmFtcCBHbG9iYWwgQ2VydGlmaWNhdGlvbiBBdXRob3Jp
35722 dHkwggEiMA0GCSqGSIb3DQEBAQUAA4IBDwAwggEKAoIBAQCYJB69FbS638eMpSe2OAtp87ZOqCwu
35723 IR1cRN8hXX4jdP5efrRKt6atH67gBhbim1vZZ3RrXYCPKZ2GG9mcDZhtdhAoWORlsH9KmHmf4MMx
35724 foArtYzAQDsRhtDLooY2YKTVMIJt2W7QDxIEM5dfT2Fa8OT5kavnHTu86M/0ay00fOJIYRyO82FE
35725 zG+gSqmUsE3a56k0enI4qEHMPJQRfevIpoy3hsvKMzvZPTeL+3o+hiznc9cKV6xkmxnr9A8ECIqs
35726 AxcZZPRaJSKNNCyy9mgdEm3Tih4U2sSPpuIjhdV6Db1q4Ons7Be7QhtnqiXtRYMh/MHJfNViPvry
35727 xS3T/dRlAgMBAAGjgZ8wgZwwEwYJKwYBBAGCNxQCBAYeBABDAEEwCwYDVR0PBAQDAgGGMA8GA1Ud
35728 EwEB/wQFMAMBAf8wHQYDVR0OBBYEFMZPoj0GY4QJnM5i5ASsjVy16bYbMDYGA1UdHwQvMC0wK6Ap
35729 oCeGJWh0dHA6Ly9jcmwueHJhbXBzZWN1cml0eS5jb20vWEdDQS5jcmwwEAYJKwYBBAGCNxUBBAMC
35730 AQEwDQYJKoZIhvcNAQEFBQADggEBAJEVOQMBG2f7Shz5CmBbodpNl2L5JFMn14JkTpAuw0kbK5rc
35731 /Kh4ZzXxHfARvbdI4xD2Dd8/0sm2qlWkSLoC295ZLhVbO50WfUfXN+pfTXYSNrsf16GBBEYgoyxt
35732 qZ4Bfj8pzgCT3/3JknOJiWSe5yvkHJEs0rnOfc5vMZnT5r7SHpDwCRR5XCOrTdLaIR9NmXmd4c8n
35733 nxCbHIgNsIpkQTG4DmyQJKSbXHGPurt+HBvbaoAPIbzp26a3QPSyi6mx5O+aGtA9aZnuqCij4Tyz
35734 8LIRnM98QObd50N9otg6tamN8jSZxNQQ4Qb9CYQQO+7ETPTsJ3xCwnR8gooJybQDJbw=
35735 -----END CERTIFICATE-----
35736
35737 Go Daddy Class 2 CA
35738 ===================
35739 -----BEGIN CERTIFICATE-----
35740 MIIEADCCAuigAwIBAgIBADANBgkqhkiG9w0BAQUFADBjMQswCQYDVQQGEwJVUzEhMB8GA1UEChMY
35741 VGhlIEdvIERhZGR5IEdyb3VwLCBJbmMuMTEwLwYDVQQLEyhHbyBEYWRkeSBDbGFzcyAyIENlcnRp
35742 ZmljYXRpb24gQXV0aG9yaXR5MB4XDTA0MDYyOTE3MDYyMFoXDTM0MDYyOTE3MDYyMFowYzELMAkG
35743 A1UEBhMCVVMxITAfBgNVBAoTGFRoZSBHbyBEYWRkeSBHcm91cCwgSW5jLjExMC8GA1UECxMoR28g
35744 RGFkZHkgQ2xhc3MgMiBDZXJ0aWZpY2F0aW9uIEF1dGhvcml0eTCCASAwDQYJKoZIhvcNAQEBBQAD
35745 ggENADCCAQgCggEBAN6d1+pXGEmhW+vXX0iG6r7d/+TvZxz0ZWizV3GgXne77ZtJ6XCAPVYYYwhv
35746 2vLM0D9/AlQiVBDYsoHUwHU9S3/Hd8M+eKsaA7Ugay9qK7HFiH7Eux6wwdhFJ2+qN1j3hybX2C32
35747 qRe3H3I2TqYXP2WYktsqbl2i/ojgC95/5Y0V4evLOtXiEqITLdiOr18SPaAIBQi2XKVlOARFmR6j
35748 YGB0xUGlcmIbYsUfb18aQr4CUWWoriMYavx4A6lNf4DD+qta/KFApMoZFv6yyO9ecw3ud72a9nmY
35749 vLEHZ6IVDd2gWMZEewo+YihfukEHU1jPEX44dMX4/7VpkI+EdOqXG68CAQOjgcAwgb0wHQYDVR0O
35750 BBYEFNLEsNKR1EwRcbNhyz2h/t2oatTjMIGNBgNVHSMEgYUwgYKAFNLEsNKR1EwRcbNhyz2h/t2o
35751 atTjoWekZTBjMQswCQYDVQQGEwJVUzEhMB8GA1UEChMYVGhlIEdvIERhZGR5IEdyb3VwLCBJbmMu
35752 MTEwLwYDVQQLEyhHbyBEYWRkeSBDbGFzcyAyIENlcnRpZmljYXRpb24gQXV0aG9yaXR5ggEAMAwG
35753 A1UdEwQFMAMBAf8wDQYJKoZIhvcNAQEFBQADggEBADJL87LKPpH8EsahB4yOd6AzBhRckB4Y9wim
35754 PQoZ+YeAEW5p5JYXMP80kWNyOO7MHAGjHZQopDH2esRU1/blMVgDoszOYtuURXO1v0XJJLXVggKt
35755 I3lpjbi2Tc7PTMozI+gciKqdi0FuFskg5YmezTvacPd+mSYgFFQlq25zheabIZ0KbIIOqPjCDPoQ
35756 HmyW74cNxA9hi63ugyuV+I6ShHI56yDqg+2DzZduCLzrTia2cyvk0/ZM/iZx4mERdEr/VxqHD3VI
35757 Ls9RaRegAhJhldXRQLIQTO7ErBBDpqWeCtWVYpoNz4iCxTIM5CufReYNnyicsbkqWletNw+vHX/b
35758 vZ8=
35759 -----END CERTIFICATE-----
35760
35761 Starfield Class 2 CA
35762 ====================
35763 -----BEGIN CERTIFICATE-----
35764 MIIEDzCCAvegAwIBAgIBADANBgkqhkiG9w0BAQUFADBoMQswCQYDVQQGEwJVUzElMCMGA1UEChMc
35765 U3RhcmZpZWxkIFRlY2hub2xvZ2llcywgSW5jLjEyMDAGA1UECxMpU3RhcmZpZWxkIENsYXNzIDIg
35766 Q2VydGlmaWNhdGlvbiBBdXRob3JpdHkwHhcNMDQwNjI5MTczOTE2WhcNMzQwNjI5MTczOTE2WjBo
35767 MQswCQYDVQQGEwJVUzElMCMGA1UEChMcU3RhcmZpZWxkIFRlY2hub2xvZ2llcywgSW5jLjEyMDAG
35768 A1UECxMpU3RhcmZpZWxkIENsYXNzIDIgQ2VydGlmaWNhdGlvbiBBdXRob3JpdHkwggEgMA0GCSqG
35769 SIb3DQEBAQUAA4IBDQAwggEIAoIBAQC3Msj+6XGmBIWtDBFk385N78gDGIc/oav7PKaf8MOh2tTY
35770 bitTkPskpD6E8J7oX+zlJ0T1KKY/e97gKvDIr1MvnsoFAZMej2YcOadN+lq2cwQlZut3f+dZxkqZ
35771 JRRU6ybH838Z1TBwj6+wRir/resp7defqgSHo9T5iaU0X9tDkYI22WY8sbi5gv2cOj4QyDvvBmVm
35772 epsZGD3/cVE8MC5fvj13c7JdBmzDI1aaK4UmkhynArPkPw2vCHmCuDY96pzTNbO8acr1zJ3o/WSN
35773 F4Azbl5KXZnJHoe0nRrA1W4TNSNe35tfPe/W93bC6j67eA0cQmdrBNj41tpvi/JEoAGrAgEDo4HF
35774 MIHCMB0GA1UdDgQWBBS/X7fRzt0fhvRbVazc1xDCDqmI5zCBkgYDVR0jBIGKMIGHgBS/X7fRzt0f
35775 hvRbVazc1xDCDqmI56FspGowaDELMAkGA1UEBhMCVVMxJTAjBgNVBAoTHFN0YXJmaWVsZCBUZWNo
35776 bm9sb2dpZXMsIEluYy4xMjAwBgNVBAsTKVN0YXJmaWVsZCBDbGFzcyAyIENlcnRpZmljYXRpb24g
35777 QXV0aG9yaXR5ggEAMAwGA1UdEwQFMAMBAf8wDQYJKoZIhvcNAQEFBQADggEBAAWdP4id0ckaVaGs
35778 afPzWdqbAYcaT1epoXkJKtv3L7IezMdeatiDh6GX70k1PncGQVhiv45YuApnP+yz3SFmH8lU+nLM
35779 PUxA2IGvd56Deruix/U0F47ZEUD0/CwqTRV/p2JdLiXTAAsgGh1o+Re49L2L7ShZ3U0WixeDyLJl
35780 xy16paq8U4Zt3VekyvggQQto8PT7dL5WXXp59fkdheMtlb71cZBDzI0fmgAKhynpVSJYACPq4xJD
35781 KVtHCN2MQWplBqjlIapBtJUhlbl90TSrE9atvNziPTnNvT51cKEYWQPJIrSPnNVeKtelttQKbfi3
35782 QBFGmh95DmK/D5fs4C8fF5Q=
35783 -----END CERTIFICATE-----
35784
35785 StartCom Certification Authority
35786 ================================
35787 -----BEGIN CERTIFICATE-----
35788 MIIHyTCCBbGgAwIBAgIBATANBgkqhkiG9w0BAQUFADB9MQswCQYDVQQGEwJJTDEWMBQGA1UEChMN
35789 U3RhcnRDb20gTHRkLjErMCkGA1UECxMiU2VjdXJlIERpZ2l0YWwgQ2VydGlmaWNhdGUgU2lnbmlu
35790 ZzEpMCcGA1UEAxMgU3RhcnRDb20gQ2VydGlmaWNhdGlvbiBBdXRob3JpdHkwHhcNMDYwOTE3MTk0
35791 NjM2WhcNMzYwOTE3MTk0NjM2WjB9MQswCQYDVQQGEwJJTDEWMBQGA1UEChMNU3RhcnRDb20gTHRk
35792 LjErMCkGA1UECxMiU2VjdXJlIERpZ2l0YWwgQ2VydGlmaWNhdGUgU2lnbmluZzEpMCcGA1UEAxMg
35793 U3RhcnRDb20gQ2VydGlmaWNhdGlvbiBBdXRob3JpdHkwggIiMA0GCSqGSIb3DQEBAQUAA4ICDwAw
35794 ggIKAoICAQDBiNsJvGxGfHiflXu1M5DycmLWwTYgIiRezul38kMKogZkpMyONvg45iPwbm2xPN1y
35795 o4UcodM9tDMr0y+v/uqwQVlntsQGfQqedIXWeUyAN3rfOQVSWff0G0ZDpNKFhdLDcfN1YjS6LIp/
35796 Ho/u7TTQEceWzVI9ujPW3U3eCztKS5/CJi/6tRYccjV3yjxd5srhJosaNnZcAdt0FCX+7bWgiA/d
35797 eMotHweXMAEtcnn6RtYTKqi5pquDSR3l8u/d5AGOGAqPY1MWhWKpDhk6zLVmpsJrdAfkK+F2PrRt
35798 2PZE4XNiHzvEvqBTViVsUQn3qqvKv3b9bZvzndu/PWa8DFaqr5hIlTpL36dYUNk4dalb6kMMAv+Z
35799 6+hsTXBbKWWc3apdzK8BMewM69KN6Oqce+Zu9ydmDBpI125C4z/eIT574Q1w+2OqqGwaVLRcJXrJ
35800 osmLFqa7LH4XXgVNWG4SHQHuEhANxjJ/GP/89PrNbpHoNkm+Gkhpi8KWTRoSsmkXwQqQ1vp5Iki/
35801 untp+HDH+no32NgN0nZPV/+Qt+OR0t3vwmC3Zzrd/qqc8NSLf3Iizsafl7b4r4qgEKjZ+xjGtrVc
35802 UjyJthkqcwEKDwOzEmDyei+B26Nu/yYwl/WL3YlXtq09s68rxbd2AvCl1iuahhQqcvbjM4xdCUsT
35803 37uMdBNSSwIDAQABo4ICUjCCAk4wDAYDVR0TBAUwAwEB/zALBgNVHQ8EBAMCAa4wHQYDVR0OBBYE
35804 FE4L7xqkQFulF2mHMMo0aEPQQa7yMGQGA1UdHwRdMFswLKAqoCiGJmh0dHA6Ly9jZXJ0LnN0YXJ0
35805 Y29tLm9yZy9zZnNjYS1jcmwuY3JsMCugKaAnhiVodHRwOi8vY3JsLnN0YXJ0Y29tLm9yZy9zZnNj
35806 YS1jcmwuY3JsMIIBXQYDVR0gBIIBVDCCAVAwggFMBgsrBgEEAYG1NwEBATCCATswLwYIKwYBBQUH
35807 AgEWI2h0dHA6Ly9jZXJ0LnN0YXJ0Y29tLm9yZy9wb2xpY3kucGRmMDUGCCsGAQUFBwIBFilodHRw
35808 Oi8vY2VydC5zdGFydGNvbS5vcmcvaW50ZXJtZWRpYXRlLnBkZjCB0AYIKwYBBQUHAgIwgcMwJxYg
35809 U3RhcnQgQ29tbWVyY2lhbCAoU3RhcnRDb20pIEx0ZC4wAwIBARqBl0xpbWl0ZWQgTGlhYmlsaXR5
35810 LCByZWFkIHRoZSBzZWN0aW9uICpMZWdhbCBMaW1pdGF0aW9ucyogb2YgdGhlIFN0YXJ0Q29tIENl
35811 cnRpZmljYXRpb24gQXV0aG9yaXR5IFBvbGljeSBhdmFpbGFibGUgYXQgaHR0cDovL2NlcnQuc3Rh
35812 cnRjb20ub3JnL3BvbGljeS5wZGYwEQYJYIZIAYb4QgEBBAQDAgAHMDgGCWCGSAGG+EIBDQQrFilT
35813 dGFydENvbSBGcmVlIFNTTCBDZXJ0aWZpY2F0aW9uIEF1dGhvcml0eTANBgkqhkiG9w0BAQUFAAOC
35814 AgEAFmyZ9GYMNPXQhV59CuzaEE44HF7fpiUFS5Eyweg78T3dRAlbB0mKKctmArexmvclmAk8jhvh
35815 3TaHK0u7aNM5Zj2gJsfyOZEdUauCe37Vzlrk4gNXcGmXCPleWKYK34wGmkUWFjgKXlf2Ysd6AgXm
35816 vB618p70qSmD+LIU424oh0TDkBreOKk8rENNZEXO3SipXPJzewT4F+irsfMuXGRuczE6Eri8sxHk
35817 fY+BUZo7jYn0TZNmezwD7dOaHZrzZVD1oNB1ny+v8OqCQ5j4aZyJecRDjkZy42Q2Eq/3JR44iZB3
35818 fsNrarnDy0RLrHiQi+fHLB5LEUTINFInzQpdn4XBidUaePKVEFMy3YCEZnXZtWgo+2EuvoSoOMCZ
35819 EoalHmdkrQYuL6lwhceWD3yJZfWOQ1QOq92lgDmUYMA0yZZwLKMS9R9Ie70cfmu3nZD0Ijuu+Pwq
35820 yvqCUqDvr0tVk+vBtfAii6w0TiYiBKGHLHVKt+V9E9e4DGTANtLJL4YSjCMJwRuCO3NJo2pXh5Tl
35821 1njFmUNj403gdy3hZZlyaQQaRwnmDwFWJPsfvw55qVguucQJAX6Vum0ABj6y6koQOdjQK/W/7HW/
35822 lwLFCRsI3FU34oH7N4RDYiDK51ZLZer+bMEkkyShNOsF/5oirpt9P/FlUQqmMGqz9IgcgA38coro
35823 g14=
35824 -----END CERTIFICATE-----
35825
35826 Taiwan GRCA
35827 ===========
35828 -----BEGIN CERTIFICATE-----
35829 MIIFcjCCA1qgAwIBAgIQH51ZWtcvwgZEpYAIaeNe9jANBgkqhkiG9w0BAQUFADA/MQswCQYDVQQG
35830 EwJUVzEwMC4GA1UECgwnR292ZXJubWVudCBSb290IENlcnRpZmljYXRpb24gQXV0aG9yaXR5MB4X
35831 DTAyMTIwNTEzMjMzM1oXDTMyMTIwNTEzMjMzM1owPzELMAkGA1UEBhMCVFcxMDAuBgNVBAoMJ0dv
35832 dmVybm1lbnQgUm9vdCBDZXJ0aWZpY2F0aW9uIEF1dGhvcml0eTCCAiIwDQYJKoZIhvcNAQEBBQAD
35833 ggIPADCCAgoCggIBAJoluOzMonWoe/fOW1mKydGGEghU7Jzy50b2iPN86aXfTEc2pBsBHH8eV4qN
35834 w8XRIePaJD9IK/ufLqGU5ywck9G/GwGHU5nOp/UKIXZ3/6m3xnOUT0b3EEk3+qhZSV1qgQdW8or5
35835 BtD3cCJNtLdBuTK4sfCxw5w/cP1T3YGq2GN49thTbqGsaoQkclSGxtKyyhwOeYHWtXBiCAEuTk8O
35836 1RGvqa/lmr/czIdtJuTJV6L7lvnM4T9TjGxMfptTCAtsF/tnyMKtsc2AtJfcdgEWFelq16TheEfO
35837 htX7MfP6Mb40qij7cEwdScevLJ1tZqa2jWR+tSBqnTuBto9AAGdLiYa4zGX+FVPpBMHWXx1E1wov
35838 J5pGfaENda1UhhXcSTvxls4Pm6Dso3pdvtUqdULle96ltqqvKKyskKw4t9VoNSZ63Pc78/1Fm9G7
35839 Q3hub/FCVGqY8A2tl+lSXunVanLeavcbYBT0peS2cWeqH+riTcFCQP5nRhc4L0c/cZyu5SHKYS1t
35840 B6iEfC3uUSXxY5Ce/eFXiGvviiNtsea9P63RPZYLhY3Naye7twWb7LuRqQoHEgKXTiCQ8P8NHuJB
35841 O9NAOueNXdpm5AKwB1KYXA6OM5zCppX7VRluTI6uSw+9wThNXo+EHWbNxWCWtFJaBYmOlXqYwZE8
35842 lSOyDvR5tMl8wUohAgMBAAGjajBoMB0GA1UdDgQWBBTMzO/MKWCkO7GStjz6MmKPrCUVOzAMBgNV
35843 HRMEBTADAQH/MDkGBGcqBwAEMTAvMC0CAQAwCQYFKw4DAhoFADAHBgVnKgMAAAQUA5vwIhP/lSg2
35844 09yewDL7MTqKUWUwDQYJKoZIhvcNAQEFBQADggIBAECASvomyc5eMN1PhnR2WPWus4MzeKR6dBcZ
35845 TulStbngCnRiqmjKeKBMmo4sIy7VahIkv9Ro04rQ2JyftB8M3jh+Vzj8jeJPXgyfqzvS/3WXy6Tj
35846 Zwj/5cAWtUgBfen5Cv8b5Wppv3ghqMKnI6mGq3ZW6A4M9hPdKmaKZEk9GhiHkASfQlK3T8v+R0F2
35847 Ne//AHY2RTKbxkaFXeIksB7jSJaYV0eUVXoPQbFEJPPB/hprv4j9wabak2BegUqZIJxIZhm1AHlU
35848 D7gsL0u8qV1bYH+Mh6XgUmMqvtg7hUAV/h62ZT/FS9p+tXo1KaMuephgIqP0fSdOLeq0dDzpD6Qz
35849 DxARvBMB1uUO07+1EqLhRSPAzAhuYbeJq4PjJB7mXQfnHyA+z2fI56wwbSdLaG5LKlwCCDTb+Hbk
35850 Z6MmnD+iMsJKxYEYMRBWqoTvLQr/uB930r+lWKBi5NdLkXWNiYCYfm3LU05er/ayl4WXudpVBrkk
35851 7tfGOB5jGxI7leFYrPLfhNVfmS8NVVvmONsuP3LpSIXLuykTjx44VbnzssQwmSNOXfJIoRIM3BKQ
35852 CZBUkQM8R+XVyWXgt0t97EfTsws+rZ7QdAAO671RrcDeLMDDav7v3Aun+kbfYNucpllQdSNpc5Oy
35853 +fwC00fmcc4QAu4njIT/rEUNE1yDMuAlpYYsfPQS
35854 -----END CERTIFICATE-----
35855
35856 Firmaprofesional Root CA
35857 ========================
35858 -----BEGIN CERTIFICATE-----
35859 MIIEVzCCAz+gAwIBAgIBATANBgkqhkiG9w0BAQUFADCBnTELMAkGA1UEBhMCRVMxIjAgBgNVBAcT
35860 GUMvIE11bnRhbmVyIDI0NCBCYXJjZWxvbmExQjBABgNVBAMTOUF1dG9yaWRhZCBkZSBDZXJ0aWZp
35861 Y2FjaW9uIEZpcm1hcHJvZmVzaW9uYWwgQ0lGIEE2MjYzNDA2ODEmMCQGCSqGSIb3DQEJARYXY2FA
35862 ZmlybWFwcm9mZXNpb25hbC5jb20wHhcNMDExMDI0MjIwMDAwWhcNMTMxMDI0MjIwMDAwWjCBnTEL
35863 MAkGA1UEBhMCRVMxIjAgBgNVBAcTGUMvIE11bnRhbmVyIDI0NCBCYXJjZWxvbmExQjBABgNVBAMT
35864 OUF1dG9yaWRhZCBkZSBDZXJ0aWZpY2FjaW9uIEZpcm1hcHJvZmVzaW9uYWwgQ0lGIEE2MjYzNDA2
35865 ODEmMCQGCSqGSIb3DQEJARYXY2FAZmlybWFwcm9mZXNpb25hbC5jb20wggEiMA0GCSqGSIb3DQEB
35866 AQUAA4IBDwAwggEKAoIBAQDnIwNvbyOlXnjOlSztlB5uCp4Bx+ow0Syd3Tfom5h5VtP8c9/Qit5V
35867 j1H5WuretXDE7aTt/6MNbg9kUDGvASdYrv5sp0ovFy3Tc9UTHI9ZpTQsHVQERc1ouKDAA6XPhUJH
35868 lShbz++AbOCQl4oBPB3zhxAwJkh91/zpnZFx/0GaqUC1N5wpIE8fUuOgfRNtVLcK3ulqTgesrBlf
35869 3H5idPayBQC6haD9HThuy1q7hryUZzM1gywfI834yJFxzJeL764P3CkDG8A563DtwW4O2GcLiam8
35870 NeTvtjS0pbbELaW+0MOUJEjb35bTALVmGotmBQ/dPz/LP6pemkr4tErvlTcbAgMBAAGjgZ8wgZww
35871 KgYDVR0RBCMwIYYfaHR0cDovL3d3dy5maXJtYXByb2Zlc2lvbmFsLmNvbTASBgNVHRMBAf8ECDAG
35872 AQH/AgEBMCsGA1UdEAQkMCKADzIwMDExMDI0MjIwMDAwWoEPMjAxMzEwMjQyMjAwMDBaMA4GA1Ud
35873 DwEB/wQEAwIBBjAdBgNVHQ4EFgQUMwugZtHq2s7eYpMEKFK1FH84aLcwDQYJKoZIhvcNAQEFBQAD
35874 ggEBAEdz/o0nVPD11HecJ3lXV7cVVuzH2Fi3AQL0M+2TUIiefEaxvT8Ub/GzR0iLjJcG1+p+o1wq
35875 u00vR+L4OQbJnC4xGgN49Lw4xiKLMzHwFgQEffl25EvXwOaD7FnMP97/T2u3Z36mhoEyIwOdyPdf
35876 wUpgpZKpsaSgYMN4h7Mi8yrrW6ntBas3D7Hi05V2Y1Z0jFhyGzflZKG+TQyTmAyX9odtsz/ny4Cm
35877 7YjHX1BiAuiZdBbQ5rQ58SfLyEDW44YQqSMSkuBpQWOnryULwMWSyx6Yo1q6xTMPoJcB3X/ge9YG
35878 VM+h4k0460tQtcsm9MracEpqoeJ5quGnM/b9Sh/22WA=
35879 -----END CERTIFICATE-----
35880
35881 Wells Fargo Root CA
35882 ===================
35883 -----BEGIN CERTIFICATE-----
35884 MIID5TCCAs2gAwIBAgIEOeSXnjANBgkqhkiG9w0BAQUFADCBgjELMAkGA1UEBhMCVVMxFDASBgNV
35885 BAoTC1dlbGxzIEZhcmdvMSwwKgYDVQQLEyNXZWxscyBGYXJnbyBDZXJ0aWZpY2F0aW9uIEF1dGhv
35886 cml0eTEvMC0GA1UEAxMmV2VsbHMgRmFyZ28gUm9vdCBDZXJ0aWZpY2F0ZSBBdXRob3JpdHkwHhcN
35887 MDAxMDExMTY0MTI4WhcNMjEwMTE0MTY0MTI4WjCBgjELMAkGA1UEBhMCVVMxFDASBgNVBAoTC1dl
35888 bGxzIEZhcmdvMSwwKgYDVQQLEyNXZWxscyBGYXJnbyBDZXJ0aWZpY2F0aW9uIEF1dGhvcml0eTEv
35889 MC0GA1UEAxMmV2VsbHMgRmFyZ28gUm9vdCBDZXJ0aWZpY2F0ZSBBdXRob3JpdHkwggEiMA0GCSqG
35890 SIb3DQEBAQUAA4IBDwAwggEKAoIBAQDVqDM7Jvk0/82bfuUER84A4n135zHCLielTWi5MbqNQ1mX
35891 x3Oqfz1cQJ4F5aHiidlMuD+b+Qy0yGIZLEWukR5zcUHESxP9cMIlrCL1dQu3U+SlK93OvRw6esP3
35892 E48mVJwWa2uv+9iWsWCaSOAlIiR5NM4OJgALTqv9i86C1y8IcGjBqAr5dE8Hq6T54oN+J3N0Prj5
35893 OEL8pahbSCOz6+MlsoCultQKnMJ4msZoGK43YjdeUXWoWGPAUe5AeH6orxqg4bB4nVCMe+ez/I4j
35894 sNtlAHCEAQgAFG5Uhpq6zPk3EPbg3oQtnaSFN9OH4xXQwReQfhkhahKpdv0SAulPIV4XAgMBAAGj
35895 YTBfMA8GA1UdEwEB/wQFMAMBAf8wTAYDVR0gBEUwQzBBBgtghkgBhvt7hwcBCzAyMDAGCCsGAQUF
35896 BwIBFiRodHRwOi8vd3d3LndlbGxzZmFyZ28uY29tL2NlcnRwb2xpY3kwDQYJKoZIhvcNAQEFBQAD
35897 ggEBANIn3ZwKdyu7IvICtUpKkfnRLb7kuxpo7w6kAOnu5+/u9vnldKTC2FJYxHT7zmu1Oyl5GFrv
35898 m+0fazbuSCUlFLZWohDo7qd/0D+j0MNdJu4HzMPBJCGHHt8qElNvQRbn7a6U+oxy+hNH8Dx+rn0R
35899 OhPs7fpvcmR7nX1/Jv16+yWt6j4pf0zjAFcysLPp7VMX2YuyFA4w6OXVE8Zkr8QA1dhYJPz1j+zx
35900 x32l2w8n0cbyQIjmH/ZhqPRCyLk306m+LFZ4wnKbWV01QIroTmMatukgalHizqSQ33ZwmVxwQ023
35901 tqcZZE6St8WRPH9IFmV7Fv3L/PvZ1dZPIWU7Sn9Ho/s=
35902 -----END CERTIFICATE-----
35903
35904 Swisscom Root CA 1
35905 ==================
35906 -----BEGIN CERTIFICATE-----
35907 MIIF2TCCA8GgAwIBAgIQXAuFXAvnWUHfV8w/f52oNjANBgkqhkiG9w0BAQUFADBkMQswCQYDVQQG
35908 EwJjaDERMA8GA1UEChMIU3dpc3Njb20xJTAjBgNVBAsTHERpZ2l0YWwgQ2VydGlmaWNhdGUgU2Vy
35909 dmljZXMxGzAZBgNVBAMTElN3aXNzY29tIFJvb3QgQ0EgMTAeFw0wNTA4MTgxMjA2MjBaFw0yNTA4
35910 MTgyMjA2MjBaMGQxCzAJBgNVBAYTAmNoMREwDwYDVQQKEwhTd2lzc2NvbTElMCMGA1UECxMcRGln
35911 aXRhbCBDZXJ0aWZpY2F0ZSBTZXJ2aWNlczEbMBkGA1UEAxMSU3dpc3Njb20gUm9vdCBDQSAxMIIC
35912 IjANBgkqhkiG9w0BAQEFAAOCAg8AMIICCgKCAgEA0LmwqAzZuz8h+BvVM5OAFmUgdbI9m2BtRsiM
35913 MW8Xw/qabFbtPMWRV8PNq5ZJkCoZSx6jbVfd8StiKHVFXqrWW/oLJdihFvkcxC7mlSpnzNApbjyF
35914 NDhhSbEAn9Y6cV9Nbc5fuankiX9qUvrKm/LcqfmdmUc/TilftKaNXXsLmREDA/7n29uj/x2lzZAe
35915 AR81sH8A25Bvxn570e56eqeqDFdvpG3FEzuwpdntMhy0XmeLVNxzh+XTF3xmUHJd1BpYwdnP2IkC
35916 b6dJtDZd0KTeByy2dbcokdaXvij1mB7qWybJvbCXc9qukSbraMH5ORXWZ0sKbU/Lz7DkQnGMU3nn
35917 7uHbHaBuHYwadzVcFh4rUx80i9Fs/PJnB3r1re3WmquhsUvhzDdf/X/NTa64H5xD+SpYVUNFvJbN
35918 cA78yeNmuk6NO4HLFWR7uZToXTNShXEuT46iBhFRyePLoW4xCGQMwtI89Tbo19AOeCMgkckkKmUp
35919 WyL3Ic6DXqTz3kvTaI9GdVyDCW4pa8RwjPWd1yAv/0bSKzjCL3UcPX7ape8eYIVpQtPM+GP+HkM5
35920 haa2Y0EQs3MevNP6yn0WR+Kn1dCjigoIlmJWbjTb2QK5MHXjBNLnj8KwEUAKrNVxAmKLMb7dxiNY
35921 MUJDLXT5xp6mig/p/r+D5kNXJLrvRjSq1xIBOO0CAwEAAaOBhjCBgzAOBgNVHQ8BAf8EBAMCAYYw
35922 HQYDVR0hBBYwFDASBgdghXQBUwABBgdghXQBUwABMBIGA1UdEwEB/wQIMAYBAf8CAQcwHwYDVR0j
35923 BBgwFoAUAyUv3m+CATpcLNwroWm1Z9SM0/0wHQYDVR0OBBYEFAMlL95vggE6XCzcK6FptWfUjNP9
35924 MA0GCSqGSIb3DQEBBQUAA4ICAQA1EMvspgQNDQ/NwNurqPKIlwzfky9NfEBWMXrrpA9gzXrzvsMn
35925 jgM+pN0S734edAY8PzHyHHuRMSG08NBsl9Tpl7IkVh5WwzW9iAUPWxAaZOHHgjD5Mq2eUCzneAXQ
35926 MbFamIp1TpBcahQq4FJHgmDmHtqBsfsUC1rxn9KVuj7QG9YVHaO+htXbD8BJZLsuUBlL0iT43R4H
35927 VtA4oJVwIHaM190e3p9xxCPvgxNcoyQVTSlAPGrEqdi3pkSlDfTgnXceQHAm/NrZNuR55LU/vJtl
35928 vrsRls/bxig5OgjOR1tTWsWZ/l2p3e9M1MalrQLmjAcSHm8D0W+go/MpvRLHUKKwf4ipmXeascCl
35929 OS5cfGniLLDqN2qk4Vrh9VDlg++luyqI54zb/W1elxmofmZ1a3Hqv7HHb6D0jqTsNFFbjCYDcKF3
35930 1QESVwA12yPeDooomf2xEG9L/zgtYE4snOtnta1J7ksfrK/7DZBaZmBwXarNeNQk7shBoJMBkpxq
35931 nvy5JMWzFYJ+vq6VK+uxwNrjAWALXmmshFZhvnEX/h0TD/7Gh0Xp/jKgGg0TpJRVcaUWi7rKibCy
35932 x/yP2FS1k2Kdzs9Z+z0YzirLNRWCXf9UIltxUvu3yf5gmwBBZPCqKuy2QkPOiWaByIufOVQDJdMW
35933 NY6E0F/6MBr1mmz0DlP5OlvRHA==
35934 -----END CERTIFICATE-----
35935
35936 DigiCert Assured ID Root CA
35937 ===========================
35938 -----BEGIN CERTIFICATE-----
35939 MIIDtzCCAp+gAwIBAgIQDOfg5RfYRv6P5WD8G/AwOTANBgkqhkiG9w0BAQUFADBlMQswCQYDVQQG
35940 EwJVUzEVMBMGA1UEChMMRGlnaUNlcnQgSW5jMRkwFwYDVQQLExB3d3cuZGlnaWNlcnQuY29tMSQw
35941 IgYDVQQDExtEaWdpQ2VydCBBc3N1cmVkIElEIFJvb3QgQ0EwHhcNMDYxMTEwMDAwMDAwWhcNMzEx
35942 MTEwMDAwMDAwWjBlMQswCQYDVQQGEwJVUzEVMBMGA1UEChMMRGlnaUNlcnQgSW5jMRkwFwYDVQQL
35943 ExB3d3cuZGlnaWNlcnQuY29tMSQwIgYDVQQDExtEaWdpQ2VydCBBc3N1cmVkIElEIFJvb3QgQ0Ew
35944 ggEiMA0GCSqGSIb3DQEBAQUAA4IBDwAwggEKAoIBAQCtDhXO5EOAXLGH87dg+XESpa7cJpSIqvTO
35945 9SA5KFhgDPiA2qkVlTJhPLWxKISKityfCgyDF3qPkKyK53lTXDGEKvYPmDI2dsze3Tyoou9q+yHy
35946 UmHfnyDXH+Kx2f4YZNISW1/5WBg1vEfNoTb5a3/UsDg+wRvDjDPZ2C8Y/igPs6eD1sNuRMBhNZYW
35947 /lmci3Zt1/GiSw0r/wty2p5g0I6QNcZ4VYcgoc/lbQrISXwxmDNsIumH0DJaoroTghHtORedmTpy
35948 oeb6pNnVFzF1roV9Iq4/AUaG9ih5yLHa5FcXxH4cDrC0kqZWs72yl+2qp/C3xag/lRbQ/6GW6whf
35949 GHdPAgMBAAGjYzBhMA4GA1UdDwEB/wQEAwIBhjAPBgNVHRMBAf8EBTADAQH/MB0GA1UdDgQWBBRF
35950 66Kv9JLLgjEtUYunpyGd823IDzAfBgNVHSMEGDAWgBRF66Kv9JLLgjEtUYunpyGd823IDzANBgkq
35951 hkiG9w0BAQUFAAOCAQEAog683+Lt8ONyc3pklL/3cmbYMuRCdWKuh+vy1dneVrOfzM4UKLkNl2Bc
35952 EkxY5NM9g0lFWJc1aRqoR+pWxnmrEthngYTffwk8lOa4JiwgvT2zKIn3X/8i4peEH+ll74fg38Fn
35953 SbNd67IJKusm7Xi+fT8r87cmNW1fiQG2SVufAQWbqz0lwcy2f8Lxb4bG+mRo64EtlOtCt/qMHt1i
35954 8b5QZ7dsvfPxH2sMNgcWfzd8qVttevESRmCD1ycEvkvOl77DZypoEd+A5wwzZr8TDRRu838fYxAe
35955 +o0bJW1sj6W3YQGx0qMmoRBxna3iw/nDmVG3KwcIzi7mULKn+gpFL6Lw8g==
35956 -----END CERTIFICATE-----
35957
35958 DigiCert Global Root CA
35959 =======================
35960 -----BEGIN CERTIFICATE-----
35961 MIIDrzCCApegAwIBAgIQCDvgVpBCRrGhdWrJWZHHSjANBgkqhkiG9w0BAQUFADBhMQswCQYDVQQG
35962 EwJVUzEVMBMGA1UEChMMRGlnaUNlcnQgSW5jMRkwFwYDVQQLExB3d3cuZGlnaWNlcnQuY29tMSAw
35963 HgYDVQQDExdEaWdpQ2VydCBHbG9iYWwgUm9vdCBDQTAeFw0wNjExMTAwMDAwMDBaFw0zMTExMTAw
35964 MDAwMDBaMGExCzAJBgNVBAYTAlVTMRUwEwYDVQQKEwxEaWdpQ2VydCBJbmMxGTAXBgNVBAsTEHd3
35965 dy5kaWdpY2VydC5jb20xIDAeBgNVBAMTF0RpZ2lDZXJ0IEdsb2JhbCBSb290IENBMIIBIjANBgkq
35966 hkiG9w0BAQEFAAOCAQ8AMIIBCgKCAQEA4jvhEXLeqKTTo1eqUKKPC3eQyaKl7hLOllsBCSDMAZOn
35967 TjC3U/dDxGkAV53ijSLdhwZAAIEJzs4bg7/fzTtxRuLWZscFs3YnFo97nh6Vfe63SKMI2tavegw5
35968 BmV/Sl0fvBf4q77uKNd0f3p4mVmFaG5cIzJLv07A6Fpt43C/dxC//AH2hdmoRBBYMql1GNXRor5H
35969 4idq9Joz+EkIYIvUX7Q6hL+hqkpMfT7PT19sdl6gSzeRntwi5m3OFBqOasv+zbMUZBfHWymeMr/y
35970 7vrTC0LUq7dBMtoM1O/4gdW7jVg/tRvoSSiicNoxBN33shbyTApOB6jtSj1etX+jkMOvJwIDAQAB
35971 o2MwYTAOBgNVHQ8BAf8EBAMCAYYwDwYDVR0TAQH/BAUwAwEB/zAdBgNVHQ4EFgQUA95QNVbRTLtm
35972 8KPiGxvDl7I90VUwHwYDVR0jBBgwFoAUA95QNVbRTLtm8KPiGxvDl7I90VUwDQYJKoZIhvcNAQEF
35973 BQADggEBAMucN6pIExIK+t1EnE9SsPTfrgT1eXkIoyQY/EsrhMAtudXH/vTBH1jLuG2cenTnmCmr
35974 EbXjcKChzUyImZOMkXDiqw8cvpOp/2PV5Adg06O/nVsJ8dWO41P0jmP6P6fbtGbfYmbW0W5BjfIt
35975 tep3Sp+dWOIrWcBAI+0tKIJFPnlUkiaY4IBIqDfv8NZ5YBberOgOzW6sRBc4L0na4UU+Krk2U886
35976 UAb3LujEV0lsYSEY1QSteDwsOoBrp+uvFRTp2InBuThs4pFsiv9kuXclVzDAGySj4dzp30d8tbQk
35977 CAUw7C29C79Fv1C5qfPrmAESrciIxpg0X40KPMbp1ZWVbd4=
35978 -----END CERTIFICATE-----
35979
35980 DigiCert High Assurance EV Root CA
35981 ==================================
35982 -----BEGIN CERTIFICATE-----
35983 MIIDxTCCAq2gAwIBAgIQAqxcJmoLQJuPC3nyrkYldzANBgkqhkiG9w0BAQUFADBsMQswCQYDVQQG
35984 EwJVUzEVMBMGA1UEChMMRGlnaUNlcnQgSW5jMRkwFwYDVQQLExB3d3cuZGlnaWNlcnQuY29tMSsw
35985 KQYDVQQDEyJEaWdpQ2VydCBIaWdoIEFzc3VyYW5jZSBFViBSb290IENBMB4XDTA2MTExMDAwMDAw
35986 MFoXDTMxMTExMDAwMDAwMFowbDELMAkGA1UEBhMCVVMxFTATBgNVBAoTDERpZ2lDZXJ0IEluYzEZ
35987 MBcGA1UECxMQd3d3LmRpZ2ljZXJ0LmNvbTErMCkGA1UEAxMiRGlnaUNlcnQgSGlnaCBBc3N1cmFu
35988 Y2UgRVYgUm9vdCBDQTCCASIwDQYJKoZIhvcNAQEBBQADggEPADCCAQoCggEBAMbM5XPm+9S75S0t
35989 Mqbf5YE/yc0lSbZxKsPVlDRnogocsF9ppkCxxLeyj9CYpKlBWTrT3JTWPNt0OKRKzE0lgvdKpVMS
35990 OO7zSW1xkX5jtqumX8OkhPhPYlG++MXs2ziS4wblCJEMxChBVfvLWokVfnHoNb9Ncgk9vjo4UFt3
35991 MRuNs8ckRZqnrG0AFFoEt7oT61EKmEFBIk5lYYeBQVCmeVyJ3hlKV9Uu5l0cUyx+mM0aBhakaHPQ
35992 NAQTXKFx01p8VdteZOE3hzBWBOURtCmAEvF5OYiiAhF8J2a3iLd48soKqDirCmTCv2ZdlYTBoSUe
35993 h10aUAsgEsxBu24LUTi4S8sCAwEAAaNjMGEwDgYDVR0PAQH/BAQDAgGGMA8GA1UdEwEB/wQFMAMB
35994 Af8wHQYDVR0OBBYEFLE+w2kD+L9HAdSYJhoIAu9jZCvDMB8GA1UdIwQYMBaAFLE+w2kD+L9HAdSY
35995 JhoIAu9jZCvDMA0GCSqGSIb3DQEBBQUAA4IBAQAcGgaX3NecnzyIZgYIVyHbIUf4KmeqvxgydkAQ
35996 V8GK83rZEWWONfqe/EW1ntlMMUu4kehDLI6zeM7b41N5cdblIZQB2lWHmiRk9opmzN6cN82oNLFp
35997 myPInngiK3BD41VHMWEZ71jFhS9OMPagMRYjyOfiZRYzy78aG6A9+MpeizGLYAiJLQwGXFK3xPkK
35998 mNEVX58Svnw2Yzi9RKR/5CYrCsSXaQ3pjOLAEFe4yHYSkVXySGnYvCoCWw9E1CAx2/S6cCZdkGCe
35999 vEsXCS+0yx5DaMkHJ8HSXPfqIbloEpw8nL+e/IBcm2PN7EeqJSdnoDfzAIJ9VNep+OkuE6N36B9K
36000 -----END CERTIFICATE-----
36001
36002 Certplus Class 2 Primary CA
36003 ===========================
36004 -----BEGIN CERTIFICATE-----
36005 MIIDkjCCAnqgAwIBAgIRAIW9S/PY2uNp9pTXX8OlRCMwDQYJKoZIhvcNAQEFBQAwPTELMAkGA1UE
36006 BhMCRlIxETAPBgNVBAoTCENlcnRwbHVzMRswGQYDVQQDExJDbGFzcyAyIFByaW1hcnkgQ0EwHhcN
36007 OTkwNzA3MTcwNTAwWhcNMTkwNzA2MjM1OTU5WjA9MQswCQYDVQQGEwJGUjERMA8GA1UEChMIQ2Vy
36008 dHBsdXMxGzAZBgNVBAMTEkNsYXNzIDIgUHJpbWFyeSBDQTCCASIwDQYJKoZIhvcNAQEBBQADggEP
36009 ADCCAQoCggEBANxQltAS+DXSCHh6tlJw/W/uz7kRy1134ezpfgSN1sxvc0NXYKwzCkTsA18cgCSR
36010 5aiRVhKC9+Ar9NuuYS6JEI1rbLqzAr3VNsVINyPi8Fo3UjMXEuLRYE2+L0ER4/YXJQyLkcAbmXuZ
36011 Vg2v7tK8R1fjeUl7NIknJITesezpWE7+Tt9avkGtrAjFGA7v0lPubNCdEgETjdyAYveVqUSISnFO
36012 YFWe2yMZeVYHDD9jC1yw4r5+FfyUM1hBOHTE4Y+L3yasH7WLO7dDWWuwJKZtkIvEcupdM5i3y95e
36013 e++U8Rs+yskhwcWYAqqi9lt3m/V+llU0HGdpwPFC40es/CgcZlUCAwEAAaOBjDCBiTAPBgNVHRME
36014 CDAGAQH/AgEKMAsGA1UdDwQEAwIBBjAdBgNVHQ4EFgQU43Mt38sOKAze3bOkynm4jrvoMIkwEQYJ
36015 YIZIAYb4QgEBBAQDAgEGMDcGA1UdHwQwMC4wLKAqoCiGJmh0dHA6Ly93d3cuY2VydHBsdXMuY29t
36016 L0NSTC9jbGFzczIuY3JsMA0GCSqGSIb3DQEBBQUAA4IBAQCnVM+IRBnL39R/AN9WM2K191EBkOvD
36017 P9GIROkkXe/nFL0gt5o8AP5tn9uQ3Nf0YtaLcF3n5QRIqWh8yfFC82x/xXp8HVGIutIKPidd3i1R
36018 TtMTZGnkLuPT55sJmabglZvOGtd/vjzOUrMRFcEPF80Du5wlFbqidon8BvEY0JNLDnyCt6X09l/+
36019 7UCmnYR0ObncHoUW2ikbhiMAybuJfm6AiB4vFLQDJKgybwOaRywwvlbGp0ICcBvqQNi6BQNwB6SW
36020 //1IMwrh3KWBkJtN3X3n57LNXMhqlfil9o3EXXgIvnsG1knPGTZQIy4I5p4FTUcY1Rbpsda2ENW7
36021 l7+ijrRU
36022 -----END CERTIFICATE-----
36023
36024 DST Root CA X3
36025 ==============
36026 -----BEGIN CERTIFICATE-----
36027 MIIDSjCCAjKgAwIBAgIQRK+wgNajJ7qJMDmGLvhAazANBgkqhkiG9w0BAQUFADA/MSQwIgYDVQQK
36028 ExtEaWdpdGFsIFNpZ25hdHVyZSBUcnVzdCBDby4xFzAVBgNVBAMTDkRTVCBSb290IENBIFgzMB4X
36029 DTAwMDkzMDIxMTIxOVoXDTIxMDkzMDE0MDExNVowPzEkMCIGA1UEChMbRGlnaXRhbCBTaWduYXR1
36030 cmUgVHJ1c3QgQ28uMRcwFQYDVQQDEw5EU1QgUm9vdCBDQSBYMzCCASIwDQYJKoZIhvcNAQEBBQAD
36031 ggEPADCCAQoCggEBAN+v6ZdQCINXtMxiZfaQguzH0yxrMMpb7NnDfcdAwRgUi+DoM3ZJKuM/IUmT
36032 rE4Orz5Iy2Xu/NMhD2XSKtkyj4zl93ewEnu1lcCJo6m67XMuegwGMoOifooUMM0RoOEqOLl5CjH9
36033 UL2AZd+3UWODyOKIYepLYYHsUmu5ouJLGiifSKOeDNoJjj4XLh7dIN9bxiqKqy69cK3FCxolkHRy
36034 xXtqqzTWMIn/5WgTe1QLyNau7Fqckh49ZLOMxt+/yUFw7BZy1SbsOFU5Q9D8/RhcQPGX69Wam40d
36035 utolucbY38EVAjqr2m7xPi71XAicPNaDaeQQmxkqtilX4+U9m5/wAl0CAwEAAaNCMEAwDwYDVR0T
36036 AQH/BAUwAwEB/zAOBgNVHQ8BAf8EBAMCAQYwHQYDVR0OBBYEFMSnsaR7LHH62+FLkHX/xBVghYkQ
36037 MA0GCSqGSIb3DQEBBQUAA4IBAQCjGiybFwBcqR7uKGY3Or+Dxz9LwwmglSBd49lZRNI+DT69ikug
36038 dB/OEIKcdBodfpga3csTS7MgROSR6cz8faXbauX+5v3gTt23ADq1cEmv8uXrAvHRAosZy5Q6XkjE
36039 GB5YGV8eAlrwDPGxrancWYaLbumR9YbK+rlmM6pZW87ipxZzR8srzJmwN0jP41ZL9c8PDHIyh8bw
36040 RLtTcm1D9SZImlJnt1ir/md2cXjbDaJWFBM5JDGFoqgCWjBH4d1QB7wCCZAA62RjYJsWvIjJEubS
36041 fZGL+T0yjWW06XyxV3bqxbYoOb8VZRzI9neWagqNdwvYkQsEjgfbKbYK7p2CNTUQ
36042 -----END CERTIFICATE-----
36043
36044 DST ACES CA X6
36045 ==============
36046 -----BEGIN CERTIFICATE-----
36047 MIIECTCCAvGgAwIBAgIQDV6ZCtadt3js2AdWO4YV2TANBgkqhkiG9w0BAQUFADBbMQswCQYDVQQG
36048 EwJVUzEgMB4GA1UEChMXRGlnaXRhbCBTaWduYXR1cmUgVHJ1c3QxETAPBgNVBAsTCERTVCBBQ0VT
36049 MRcwFQYDVQQDEw5EU1QgQUNFUyBDQSBYNjAeFw0wMzExMjAyMTE5NThaFw0xNzExMjAyMTE5NTha
36050 MFsxCzAJBgNVBAYTAlVTMSAwHgYDVQQKExdEaWdpdGFsIFNpZ25hdHVyZSBUcnVzdDERMA8GA1UE
36051 CxMIRFNUIEFDRVMxFzAVBgNVBAMTDkRTVCBBQ0VTIENBIFg2MIIBIjANBgkqhkiG9w0BAQEFAAOC
36052 AQ8AMIIBCgKCAQEAuT31LMmU3HWKlV1j6IR3dma5WZFcRt2SPp/5DgO0PWGSvSMmtWPuktKe1jzI
36053 DZBfZIGxqAgNTNj50wUoUrQBJcWVHAx+PhCEdc/BGZFjz+iokYi5Q1K7gLFViYsx+tC3dr5BPTCa
36054 pCIlF3PoHuLTrCq9Wzgh1SpL11V94zpVvddtawJXa+ZHfAjIgrrep4c9oW24MFbCswKBXy314pow
36055 GCi4ZtPLAZZv6opFVdbgnf9nKxcCpk4aahELfrd755jWjHZvwTvbUJN+5dCOHze4vbrGn2zpfDPy
36056 MjwmR/onJALJfh1biEITajV8fTXpLmaRcpPVMibEdPVTo7NdmvYJywIDAQABo4HIMIHFMA8GA1Ud
36057 EwEB/wQFMAMBAf8wDgYDVR0PAQH/BAQDAgHGMB8GA1UdEQQYMBaBFHBraS1vcHNAdHJ1c3Rkc3Qu
36058 Y29tMGIGA1UdIARbMFkwVwYKYIZIAWUDAgEBATBJMEcGCCsGAQUFBwIBFjtodHRwOi8vd3d3LnRy
36059 dXN0ZHN0LmNvbS9jZXJ0aWZpY2F0ZXMvcG9saWN5L0FDRVMtaW5kZXguaHRtbDAdBgNVHQ4EFgQU
36060 CXIGThhDD+XWzMNqizF7eI+og7gwDQYJKoZIhvcNAQEFBQADggEBAKPYjtay284F5zLNAdMEA+V2
36061 5FYrnJmQ6AgwbN99Pe7lv7UkQIRJ4dEorsTCOlMwiPH1d25Ryvr/ma8kXxug/fKshMrfqfBfBC6t
36062 Fr8hlxCBPeP/h40y3JTlR4peahPJlJU90u7INJXQgNStMgiAVDzgvVJT11J8smk/f3rPanTK+gQq
36063 nExaBqXpIK1FZg9p8d2/6eMyi/rgwYZNcjwu2JN4Cir42NInPRmJX1p7ijvMDNpRrscL9yuwNwXs
36064 vFcj4jjSm2jzVhKIT0J8uDHEtdvkyCE06UgRNe76x5JXxZ805Mf29w4LTJxoeHtxMcfrHuBnQfO3
36065 oKfN5XozNmr6mis=
36066 -----END CERTIFICATE-----
36067
36068 TURKTRUST Certificate Services Provider Root 1
36069 ==============================================
36070 -----BEGIN CERTIFICATE-----
36071 MIID+zCCAuOgAwIBAgIBATANBgkqhkiG9w0BAQUFADCBtzE/MD0GA1UEAww2VMOcUktUUlVTVCBF
36072 bGVrdHJvbmlrIFNlcnRpZmlrYSBIaXptZXQgU2HEn2xhecSxY8Sxc8SxMQswCQYDVQQGDAJUUjEP
36073 MA0GA1UEBwwGQU5LQVJBMVYwVAYDVQQKDE0oYykgMjAwNSBUw5xSS1RSVVNUIEJpbGdpIMSwbGV0
36074 acWfaW0gdmUgQmlsacWfaW0gR8O8dmVubGnEn2kgSGl6bWV0bGVyaSBBLsWeLjAeFw0wNTA1MTMx
36075 MDI3MTdaFw0xNTAzMjIxMDI3MTdaMIG3MT8wPQYDVQQDDDZUw5xSS1RSVVNUIEVsZWt0cm9uaWsg
36076 U2VydGlmaWthIEhpem1ldCBTYcSfbGF5xLFjxLFzxLExCzAJBgNVBAYMAlRSMQ8wDQYDVQQHDAZB
36077 TktBUkExVjBUBgNVBAoMTShjKSAyMDA1IFTDnFJLVFJVU1QgQmlsZ2kgxLBsZXRpxZ9pbSB2ZSBC
36078 aWxpxZ9pbSBHw7x2ZW5sacSfaSBIaXptZXRsZXJpIEEuxZ4uMIIBIjANBgkqhkiG9w0BAQEFAAOC
36079 AQ8AMIIBCgKCAQEAylIF1mMD2Bxf3dJ7XfIMYGFbazt0K3gNfUW9InTojAPBxhEqPZW8qZSwu5GX
36080 yGl8hMW0kWxsE2qkVa2kheiVfrMArwDCBRj1cJ02i67L5BuBf5OI+2pVu32Fks66WJ/bMsW9Xe8i
36081 Si9BB35JYbOG7E6mQW6EvAPs9TscyB/C7qju6hJKjRTP8wrgUDn5CDX4EVmt5yLqS8oUBt5CurKZ
36082 8y1UiBAG6uEaPj1nH/vO+3yC6BFdSsG5FOpU2WabfIl9BJpiyelSPJ6c79L1JuTm5Rh8i27fbMx4
36083 W09ysstcP4wFjdFMjK2Sx+F4f2VsSQZQLJ4ywtdKxnWKWU51b0dewQIDAQABoxAwDjAMBgNVHRME
36084 BTADAQH/MA0GCSqGSIb3DQEBBQUAA4IBAQAV9VX/N5aAWSGk/KEVTCD21F/aAyT8z5Aa9CEKmu46
36085 sWrv7/hg0Uw2ZkUd82YCdAR7kjCo3gp2D++Vbr3JN+YaDayJSFvMgzbC9UZcWYJWtNX+I7TYVBxE
36086 q8Sn5RTOPEFhfEPmzcSBCYsk+1Ql1haolgxnB2+zUEfjHCQo3SqYpGH+2+oSN7wBGjSFvW5P55Fy
36087 B0SFHljKVETd96y5y4khctuPwGkplyqjrhgjlxxBKot8KsF8kOipKMDTkcatKIdAaLX/7KfS0zgY
36088 nNN9aV3wxqUeJBujR/xpB2jn5Jq07Q+hh4cCzofSSE7hvP/L8XKSRGQDJereW26fyfJOrN3H
36089 -----END CERTIFICATE-----
36090
36091 TURKTRUST Certificate Services Provider Root 2
36092 ==============================================
36093 -----BEGIN CERTIFICATE-----
36094 MIIEPDCCAySgAwIBAgIBATANBgkqhkiG9w0BAQUFADCBvjE/MD0GA1UEAww2VMOcUktUUlVTVCBF
36095 bGVrdHJvbmlrIFNlcnRpZmlrYSBIaXptZXQgU2HEn2xhecSxY8Sxc8SxMQswCQYDVQQGEwJUUjEP
36096 MA0GA1UEBwwGQW5rYXJhMV0wWwYDVQQKDFRUw5xSS1RSVVNUIEJpbGdpIMSwbGV0acWfaW0gdmUg
36097 QmlsacWfaW0gR8O8dmVubGnEn2kgSGl6bWV0bGVyaSBBLsWeLiAoYykgS2FzxLFtIDIwMDUwHhcN
36098 MDUxMTA3MTAwNzU3WhcNMTUwOTE2MTAwNzU3WjCBvjE/MD0GA1UEAww2VMOcUktUUlVTVCBFbGVr
36099 dHJvbmlrIFNlcnRpZmlrYSBIaXptZXQgU2HEn2xhecSxY8Sxc8SxMQswCQYDVQQGEwJUUjEPMA0G
36100 A1UEBwwGQW5rYXJhMV0wWwYDVQQKDFRUw5xSS1RSVVNUIEJpbGdpIMSwbGV0acWfaW0gdmUgQmls
36101 acWfaW0gR8O8dmVubGnEn2kgSGl6bWV0bGVyaSBBLsWeLiAoYykgS2FzxLFtIDIwMDUwggEiMA0G
36102 CSqGSIb3DQEBAQUAA4IBDwAwggEKAoIBAQCpNn7DkUNMwxmYCMjHWHtPFoylzkkBH3MOrHUTpvqe
36103 LCDe2JAOCtFp0if7qnefJ1Il4std2NiDUBd9irWCPwSOtNXwSadktx4uXyCcUHVPr+G1QRT0mJKI
36104 x+XlZEdhR3n9wFHxwZnn3M5q+6+1ATDcRhzviuyV79z/rxAc653YsKpqhRgNF8k+v/Gb0AmJQv2g
36105 QrSdiVFVKc8bcLyEVK3BEx+Y9C52YItdP5qtygy/p1Zbj3e41Z55SZI/4PGXJHpsmxcPbe9TmJEr
36106 5A++WXkHeLuXlfSfadRYhwqp48y2WBmfJiGxxFmNskF1wK1pzpwACPI2/z7woQ8arBT9pmAPAgMB
36107 AAGjQzBBMB0GA1UdDgQWBBTZN7NOBf3Zz58SFq62iS/rJTqIHDAPBgNVHQ8BAf8EBQMDBwYAMA8G
36108 A1UdEwEB/wQFMAMBAf8wDQYJKoZIhvcNAQEFBQADggEBAHJglrfJ3NgpXiOFX7KzLXb7iNcX/ntt
36109 Rbj2hWyfIvwqECLsqrkw9qtY1jkQMZkpAL2JZkH7dN6RwRgLn7Vhy506vvWolKMiVW4XSf/SKfE4
36110 Jl3vpao6+XF75tpYHdN0wgH6PmlYX63LaL4ULptswLbcoCb6dxriJNoaN+BnrdFzgw2lGh1uEpJ+
36111 hGIAF728JRhX8tepb1mIvDS3LoV4nZbcFMMsilKbloxSZj2GFotHuFEJjOp9zYhys2AzsfAKRO8P
36112 9Qk3iCQOLGsgOqL6EfJANZxEaGM7rDNvY7wsu/LSy3Z9fYjYHcgFHW68lKlmjHdxx/qR+i9Rnuk5
36113 UrbnBEI=
36114 -----END CERTIFICATE-----
36115
36116 SwissSign Platinum CA - G2
36117 ==========================
36118 -----BEGIN CERTIFICATE-----
36119 MIIFwTCCA6mgAwIBAgIITrIAZwwDXU8wDQYJKoZIhvcNAQEFBQAwSTELMAkGA1UEBhMCQ0gxFTAT
36120 BgNVBAoTDFN3aXNzU2lnbiBBRzEjMCEGA1UEAxMaU3dpc3NTaWduIFBsYXRpbnVtIENBIC0gRzIw
36121 HhcNMDYxMDI1MDgzNjAwWhcNMzYxMDI1MDgzNjAwWjBJMQswCQYDVQQGEwJDSDEVMBMGA1UEChMM
36122 U3dpc3NTaWduIEFHMSMwIQYDVQQDExpTd2lzc1NpZ24gUGxhdGludW0gQ0EgLSBHMjCCAiIwDQYJ
36123 KoZIhvcNAQEBBQADggIPADCCAgoCggIBAMrfogLi2vj8Bxax3mCq3pZcZB/HL37PZ/pEQtZ2Y5Wu
36124 669yIIpFR4ZieIbWIDkm9K6j/SPnpZy1IiEZtzeTIsBQnIJ71NUERFzLtMKfkr4k2HtnIuJpX+UF
36125 eNSH2XFwMyVTtIc7KZAoNppVRDBopIOXfw0enHb/FZ1glwCNioUD7IC+6ixuEFGSzH7VozPY1kne
36126 WCqv9hbrS3uQMpe5up1Y8fhXSQQeol0GcN1x2/ndi5objM89o03Oy3z2u5yg+gnOI2Ky6Q0f4nIo
36127 j5+saCB9bzuohTEJfwvH6GXp43gOCWcwizSC+13gzJ2BbWLuCB4ELE6b7P6pT1/9aXjvCR+htL/6
36128 8++QHkwFix7qepF6w9fl+zC8bBsQWJj3Gl/QKTIDE0ZNYWqFTFJ0LwYfexHihJfGmfNtf9dng34T
36129 aNhxKFrYzt3oEBSa/m0jh26OWnA81Y0JAKeqvLAxN23IhBQeW71FYyBrS3SMvds6DsHPWhaPpZjy
36130 domyExI7C3d3rLvlPClKknLKYRorXkzig3R3+jVIeoVNjZpTxN94ypeRSCtFKwH3HBqi7Ri6Cr2D
36131 +m+8jVeTO9TUps4e8aCxzqv9KyiaTxvXw3LbpMS/XUz13XuWae5ogObnmLo2t/5u7Su9IPhlGdpV
36132 CX4l3P5hYnL5fhgC72O00Puv5TtjjGePAgMBAAGjgawwgakwDgYDVR0PAQH/BAQDAgEGMA8GA1Ud
36133 EwEB/wQFMAMBAf8wHQYDVR0OBBYEFFCvzAeHFUdvOMW0ZdHelarp35zMMB8GA1UdIwQYMBaAFFCv
36134 zAeHFUdvOMW0ZdHelarp35zMMEYGA1UdIAQ/MD0wOwYJYIV0AVkBAQEBMC4wLAYIKwYBBQUHAgEW
36135 IGh0dHA6Ly9yZXBvc2l0b3J5LnN3aXNzc2lnbi5jb20vMA0GCSqGSIb3DQEBBQUAA4ICAQAIhab1
36136 Fgz8RBrBY+D5VUYI/HAcQiiWjrfFwUF1TglxeeVtlspLpYhg0DB0uMoI3LQwnkAHFmtllXcBrqS3
36137 NQuB2nEVqXQXOHtYyvkv+8Bldo1bAbl93oI9ZLi+FHSjClTTLJUYFzX1UWs/j6KWYTl4a0vlpqD4
36138 U99REJNi54Av4tHgvI42Rncz7Lj7jposiU0xEQ8mngS7twSNC/K5/FqdOxa3L8iYq/6KUFkuozv8
36139 KV2LwUvJ4ooTHbG/u0IdUt1O2BReEMYxB+9xJ/cbOQncguqLs5WGXv312l0xpuAxtpTmREl0xRbl
36140 9x8DYSjFyMsSoEJL+WuICI20MhjzdZ/EfwBPBZWcoxcCw7NTm6ogOSkrZvqdr16zktK1puEa+S1B
36141 aYEUtLS17Yk9zvupnTVCRLEcFHOBzyoBNZox1S2PbYTfgE1X4z/FhHXaicYwu+uPyyIIoK6q8QNs
36142 OktNCaUOcsZWayFCTiMlFGiudgp8DAdwZPmaL/YFOSbGDI8Zf0NebvRbFS/bYV3mZy8/CJT5YLSY
36143 Mdp08YSTcU1f+2BY0fvEwW2JorsgH51xkcsymxM9Pn2SUjWskpSi0xjCfMfqr3YFFt1nJ8J+HAci
36144 IfNAChs0B0QTwoRqjt8ZWr9/6x3iGjjRXK9HkmuAtTClyY3YqzGBH9/CZjfTk6mFhnll0g==
36145 -----END CERTIFICATE-----
36146
36147 SwissSign Gold CA - G2
36148 ======================
36149 -----BEGIN CERTIFICATE-----
36150 MIIFujCCA6KgAwIBAgIJALtAHEP1Xk+wMA0GCSqGSIb3DQEBBQUAMEUxCzAJBgNVBAYTAkNIMRUw
36151 EwYDVQQKEwxTd2lzc1NpZ24gQUcxHzAdBgNVBAMTFlN3aXNzU2lnbiBHb2xkIENBIC0gRzIwHhcN
36152 MDYxMDI1MDgzMDM1WhcNMzYxMDI1MDgzMDM1WjBFMQswCQYDVQQGEwJDSDEVMBMGA1UEChMMU3dp
36153 c3NTaWduIEFHMR8wHQYDVQQDExZTd2lzc1NpZ24gR29sZCBDQSAtIEcyMIICIjANBgkqhkiG9w0B
36154 AQEFAAOCAg8AMIICCgKCAgEAr+TufoskDhJuqVAtFkQ7kpJcyrhdhJJCEyq8ZVeCQD5XJM1QiyUq
36155 t2/876LQwB8CJEoTlo8jE+YoWACjR8cGp4QjK7u9lit/VcyLwVcfDmJlD909Vopz2q5+bbqBHH5C
36156 jCA12UNNhPqE21Is8w4ndwtrvxEvcnifLtg+5hg3Wipy+dpikJKVyh+c6bM8K8vzARO/Ws/BtQpg
36157 vd21mWRTuKCWs2/iJneRjOBiEAKfNA+k1ZIzUd6+jbqEemA8atufK+ze3gE/bk3lUIbLtK/tREDF
36158 ylqM2tIrfKjuvqblCqoOpd8FUrdVxyJdMmqXl2MT28nbeTZ7hTpKxVKJ+STnnXepgv9VHKVxaSvR
36159 AiTysybUa9oEVeXBCsdtMDeQKuSeFDNeFhdVxVu1yzSJkvGdJo+hB9TGsnhQ2wwMC3wLjEHXuend
36160 jIj3o02yMszYF9rNt85mndT9Xv+9lz4pded+p2JYryU0pUHHPbwNUMoDAw8IWh+Vc3hiv69yFGkO
36161 peUDDniOJihC8AcLYiAQZzlG+qkDzAQ4embvIIO1jEpWjpEA/I5cgt6IoMPiaG59je883WX0XaxR
36162 7ySArqpWl2/5rX3aYT+YdzylkbYcjCbaZaIJbcHiVOO5ykxMgI93e2CaHt+28kgeDrpOVG2Y4OGi
36163 GqJ3UM/EY5LsRxmd6+ZrzsECAwEAAaOBrDCBqTAOBgNVHQ8BAf8EBAMCAQYwDwYDVR0TAQH/BAUw
36164 AwEB/zAdBgNVHQ4EFgQUWyV7lqRlUX64OfPAeGZe6Drn8O4wHwYDVR0jBBgwFoAUWyV7lqRlUX64
36165 OfPAeGZe6Drn8O4wRgYDVR0gBD8wPTA7BglghXQBWQECAQEwLjAsBggrBgEFBQcCARYgaHR0cDov
36166 L3JlcG9zaXRvcnkuc3dpc3NzaWduLmNvbS8wDQYJKoZIhvcNAQEFBQADggIBACe645R88a7A3hfm
36167 5djV9VSwg/S7zV4Fe0+fdWavPOhWfvxyeDgD2StiGwC5+OlgzczOUYrHUDFu4Up+GC9pWbY9ZIEr
36168 44OE5iKHjn3g7gKZYbge9LgriBIWhMIxkziWMaa5O1M/wySTVltpkuzFwbs4AOPsF6m43Md8AYOf
36169 Mke6UiI0HTJ6CVanfCU2qT1L2sCCbwq7EsiHSycR+R4tx5M/nttfJmtS2S6K8RTGRI0Vqbe/vd6m
36170 Gu6uLftIdxf+u+yvGPUqUfA5hJeVbG4bwyvEdGB5JbAKJ9/fXtI5z0V9QkvfsywexcZdylU6oJxp
36171 mo/a77KwPJ+HbBIrZXAVUjEaJM9vMSNQH4xPjyPDdEFjHFWoFN0+4FFQz/EbMFYOkrCChdiDyyJk
36172 vC24JdVUorgG6q2SpCSgwYa1ShNqR88uC1aVVMvOmttqtKay20EIhid392qgQmwLOM7XdVAyksLf
36173 KzAiSNDVQTglXaTpXZ/GlHXQRf0wl0OPkKsKx4ZzYEppLd6leNcG2mqeSz53OiATIgHQv2ieY2Br
36174 NU0LbbqhPcCT4H8js1WtciVORvnSFu+wZMEBnunKoGqYDs/YYPIvSbjkQuE4NRb0yG5P94FW6Lqj
36175 viOvrv1vA+ACOzB2+httQc8Bsem4yWb02ybzOqR08kkkW8mw0FfB+j564ZfJ
36176 -----END CERTIFICATE-----
36177
36178 SwissSign Silver CA - G2
36179 ========================
36180 -----BEGIN CERTIFICATE-----
36181 MIIFvTCCA6WgAwIBAgIITxvUL1S7L0swDQYJKoZIhvcNAQEFBQAwRzELMAkGA1UEBhMCQ0gxFTAT
36182 BgNVBAoTDFN3aXNzU2lnbiBBRzEhMB8GA1UEAxMYU3dpc3NTaWduIFNpbHZlciBDQSAtIEcyMB4X
36183 DTA2MTAyNTA4MzI0NloXDTM2MTAyNTA4MzI0NlowRzELMAkGA1UEBhMCQ0gxFTATBgNVBAoTDFN3
36184 aXNzU2lnbiBBRzEhMB8GA1UEAxMYU3dpc3NTaWduIFNpbHZlciBDQSAtIEcyMIICIjANBgkqhkiG
36185 9w0BAQEFAAOCAg8AMIICCgKCAgEAxPGHf9N4Mfc4yfjDmUO8x/e8N+dOcbpLj6VzHVxumK4DV644
36186 N0MvFz0fyM5oEMF4rhkDKxD6LHmD9ui5aLlV8gREpzn5/ASLHvGiTSf5YXu6t+WiE7brYT7QbNHm
36187 +/pe7R20nqA1W6GSy/BJkv6FCgU+5tkL4k+73JU3/JHpMjUi0R86TieFnbAVlDLaYQ1HTWBCrpJH
36188 6INaUFjpiou5XaHc3ZlKHzZnu0jkg7Y360g6rw9njxcH6ATK72oxh9TAtvmUcXtnZLi2kUpCe2Uu
36189 MGoM9ZDulebyzYLs2aFK7PayS+VFheZteJMELpyCbTapxDFkH4aDCyr0NQp4yVXPQbBH6TCfmb5h
36190 qAaEuSh6XzjZG6k4sIN/c8HDO0gqgg8hm7jMqDXDhBuDsz6+pJVpATqJAHgE2cn0mRmrVn5bi4Y5
36191 FZGkECwJMoBgs5PAKrYYC51+jUnyEEp/+dVGLxmSo5mnJqy7jDzmDrxHB9xzUfFwZC8I+bRHHTBs
36192 ROopN4WSaGa8gzj+ezku01DwH/teYLappvonQfGbGHLy9YR0SslnxFSuSGTfjNFusB3hB48IHpmc
36193 celM2KX3RxIfdNFRnobzwqIjQAtz20um53MGjMGg6cFZrEb65i/4z3GcRm25xBWNOHkDRUjvxF3X
36194 CO6HOSKGsg0PWEP3calILv3q1h8CAwEAAaOBrDCBqTAOBgNVHQ8BAf8EBAMCAQYwDwYDVR0TAQH/
36195 BAUwAwEB/zAdBgNVHQ4EFgQUF6DNweRBtjpbO8tFnb0cwpj6hlgwHwYDVR0jBBgwFoAUF6DNweRB
36196 tjpbO8tFnb0cwpj6hlgwRgYDVR0gBD8wPTA7BglghXQBWQEDAQEwLjAsBggrBgEFBQcCARYgaHR0
36197 cDovL3JlcG9zaXRvcnkuc3dpc3NzaWduLmNvbS8wDQYJKoZIhvcNAQEFBQADggIBAHPGgeAn0i0P
36198 4JUw4ppBf1AsX19iYamGamkYDHRJ1l2E6kFSGG9YrVBWIGrGvShpWJHckRE1qTodvBqlYJ7YH39F
36199 kWnZfrt4csEGDyrOj4VwYaygzQu4OSlWhDJOhrs9xCrZ1x9y7v5RoSJBsXECYxqCsGKrXlcSH9/L
36200 3XWgwF15kIwb4FDm3jH+mHtwX6WQ2K34ArZv02DdQEsixT2tOnqfGhpHkXkzuoLcMmkDlm4fS/Bx
36201 /uNncqCxv1yL5PqZIseEuRuNI5c/7SXgz2W79WEE790eslpBIlqhn10s6FvJbakMDHiqYMZWjwFa
36202 DGi8aRl5xB9+lwW/xekkUV7U1UtT7dkjWjYDZaPBA61BMPNGG4WQr2W11bHkFlt4dR2Xem1ZqSqP
36203 e97Dh4kQmUlzeMg9vVE1dCrV8X5pGyq7O70luJpaPXJhkGaH7gzWTdQRdAtq/gsD/KNVV4n+Ssuu
36204 WxcFyPKNIzFTONItaj+CuY0IavdeQXRuwxF+B6wpYJE/OMpXEA29MC/HpeZBoNquBYeaoKRlbEwJ
36205 DIm6uNO5wJOKMPqN5ZprFQFOZ6raYlY+hAhm0sQ2fac+EPyI4NSA5QC9qvNOBqN6avlicuMJT+ub
36206 DgEj8Z+7fNzcbBGXJbLytGMU0gYqZ4yD9c7qB9iaah7s5Aq7KkzrCWA5zspi2C5u
36207 -----END CERTIFICATE-----
36208
36209 GeoTrust Primary Certification Authority
36210 ========================================
36211 -----BEGIN CERTIFICATE-----
36212 MIIDfDCCAmSgAwIBAgIQGKy1av1pthU6Y2yv2vrEoTANBgkqhkiG9w0BAQUFADBYMQswCQYDVQQG
36213 EwJVUzEWMBQGA1UEChMNR2VvVHJ1c3QgSW5jLjExMC8GA1UEAxMoR2VvVHJ1c3QgUHJpbWFyeSBD
36214 ZXJ0aWZpY2F0aW9uIEF1dGhvcml0eTAeFw0wNjExMjcwMDAwMDBaFw0zNjA3MTYyMzU5NTlaMFgx
36215 CzAJBgNVBAYTAlVTMRYwFAYDVQQKEw1HZW9UcnVzdCBJbmMuMTEwLwYDVQQDEyhHZW9UcnVzdCBQ
36216 cmltYXJ5IENlcnRpZmljYXRpb24gQXV0aG9yaXR5MIIBIjANBgkqhkiG9w0BAQEFAAOCAQ8AMIIB
36217 CgKCAQEAvrgVe//UfH1nrYNke8hCUy3f9oQIIGHWAVlqnEQRr+92/ZV+zmEwu3qDXwK9AWbK7hWN
36218 b6EwnL2hhZ6UOvNWiAAxz9juapYC2e0DjPt1befquFUWBRaa9OBesYjAZIVcFU2Ix7e64HXprQU9
36219 nceJSOC7KMgD4TCTZF5SwFlwIjVXiIrxlQqD17wxcwE07e9GceBrAqg1cmuXm2bgyxx5X9gaBGge
36220 RwLmnWDiNpcB3841kt++Z8dtd1k7j53WkBWUvEI0EME5+bEnPn7WinXFsq+W06Lem+SYvn3h6YGt
36221 tm/81w7a4DSwDRp35+MImO9Y+pyEtzavwt+s0vQQBnBxNQIDAQABo0IwQDAPBgNVHRMBAf8EBTAD
36222 AQH/MA4GA1UdDwEB/wQEAwIBBjAdBgNVHQ4EFgQULNVQQZcVi/CPNmFbSvtr2ZnJM5IwDQYJKoZI
36223 hvcNAQEFBQADggEBAFpwfyzdtzRP9YZRqSa+S7iq8XEN3GHHoOo0Hnp3DwQ16CePbJC/kRYkRj5K
36224 Ts4rFtULUh38H2eiAkUxT87z+gOneZ1TatnaYzr4gNfTmeGl4b7UVXGYNTq+k+qurUKykG/g/CFN
36225 NWMziUnWm07Kx+dOCQD32sfvmWKZd7aVIl6KoKv0uHiYyjgZmclynnjNS6yvGaBzEi38wkG6gZHa
36226 Floxt/m0cYASSJlyc1pZU8FjUjPtp8nSOQJw+uCxQmYpqptR7TBUIhRf2asdweSU8Pj1K/fqynhG
36227 1riR/aYNKxoUAT6A8EKglQdebc3MS6RFjasS6LPeWuWgfOgPIh1a6Vk=
36228 -----END CERTIFICATE-----
36229
36230 thawte Primary Root CA
36231 ======================
36232 -----BEGIN CERTIFICATE-----
36233 MIIEIDCCAwigAwIBAgIQNE7VVyDV7exJ9C/ON9srbTANBgkqhkiG9w0BAQUFADCBqTELMAkGA1UE
36234 BhMCVVMxFTATBgNVBAoTDHRoYXd0ZSwgSW5jLjEoMCYGA1UECxMfQ2VydGlmaWNhdGlvbiBTZXJ2
36235 aWNlcyBEaXZpc2lvbjE4MDYGA1UECxMvKGMpIDIwMDYgdGhhd3RlLCBJbmMuIC0gRm9yIGF1dGhv
36236 cml6ZWQgdXNlIG9ubHkxHzAdBgNVBAMTFnRoYXd0ZSBQcmltYXJ5IFJvb3QgQ0EwHhcNMDYxMTE3
36237 MDAwMDAwWhcNMzYwNzE2MjM1OTU5WjCBqTELMAkGA1UEBhMCVVMxFTATBgNVBAoTDHRoYXd0ZSwg
36238 SW5jLjEoMCYGA1UECxMfQ2VydGlmaWNhdGlvbiBTZXJ2aWNlcyBEaXZpc2lvbjE4MDYGA1UECxMv
36239 KGMpIDIwMDYgdGhhd3RlLCBJbmMuIC0gRm9yIGF1dGhvcml6ZWQgdXNlIG9ubHkxHzAdBgNVBAMT
36240 FnRoYXd0ZSBQcmltYXJ5IFJvb3QgQ0EwggEiMA0GCSqGSIb3DQEBAQUAA4IBDwAwggEKAoIBAQCs
36241 oPD7gFnUnMekz52hWXMJEEUMDSxuaPFsW0hoSVk3/AszGcJ3f8wQLZU0HObrTQmnHNK4yZc2AreJ
36242 1CRfBsDMRJSUjQJib+ta3RGNKJpchJAQeg29dGYvajig4tVUROsdB58Hum/u6f1OCyn1PoSgAfGc
36243 q/gcfomk6KHYcWUNo1F77rzSImANuVud37r8UVsLr5iy6S7pBOhih94ryNdOwUxkHt3Ph1i6Sk/K
36244 aAcdHJ1KxtUvkcx8cXIcxcBn6zL9yZJclNqFwJu/U30rCfSMnZEfl2pSy94JNqR32HuHUETVPm4p
36245 afs5SSYeCaWAe0At6+gnhcn+Yf1+5nyXHdWdAgMBAAGjQjBAMA8GA1UdEwEB/wQFMAMBAf8wDgYD
36246 VR0PAQH/BAQDAgEGMB0GA1UdDgQWBBR7W0XPr87Lev0xkhpqtvNG61dIUDANBgkqhkiG9w0BAQUF
36247 AAOCAQEAeRHAS7ORtvzw6WfUDW5FvlXok9LOAz/t2iWwHVfLHjp2oEzsUHboZHIMpKnxuIvW1oeE
36248 uzLlQRHAd9mzYJ3rG9XRbkREqaYB7FViHXe4XI5ISXycO1cRrK1zN44veFyQaEfZYGDm/Ac9IiAX
36249 xPcW6cTYcvnIc3zfFi8VqT79aie2oetaupgf1eNNZAqdE8hhuvU5HIe6uL17In/2/qxAeeWsEG89
36250 jxt5dovEN7MhGITlNgDrYyCZuen+MwS7QcjBAvlEYyCegc5C09Y/LHbTY5xZ3Y+m4Q6gLkH3LpVH
36251 z7z9M/P2C2F+fpErgUfCJzDupxBdN49cOSvkBPB7jVaMaA==
36252 -----END CERTIFICATE-----
36253
36254 VeriSign Class 3 Public Primary Certification Authority - G5
36255 ============================================================
36256 -----BEGIN CERTIFICATE-----
36257 MIIE0zCCA7ugAwIBAgIQGNrRniZ96LtKIVjNzGs7SjANBgkqhkiG9w0BAQUFADCByjELMAkGA1UE
36258 BhMCVVMxFzAVBgNVBAoTDlZlcmlTaWduLCBJbmMuMR8wHQYDVQQLExZWZXJpU2lnbiBUcnVzdCBO
36259 ZXR3b3JrMTowOAYDVQQLEzEoYykgMjAwNiBWZXJpU2lnbiwgSW5jLiAtIEZvciBhdXRob3JpemVk
36260 IHVzZSBvbmx5MUUwQwYDVQQDEzxWZXJpU2lnbiBDbGFzcyAzIFB1YmxpYyBQcmltYXJ5IENlcnRp
36261 ZmljYXRpb24gQXV0aG9yaXR5IC0gRzUwHhcNMDYxMTA4MDAwMDAwWhcNMzYwNzE2MjM1OTU5WjCB
36262 yjELMAkGA1UEBhMCVVMxFzAVBgNVBAoTDlZlcmlTaWduLCBJbmMuMR8wHQYDVQQLExZWZXJpU2ln
36263 biBUcnVzdCBOZXR3b3JrMTowOAYDVQQLEzEoYykgMjAwNiBWZXJpU2lnbiwgSW5jLiAtIEZvciBh
36264 dXRob3JpemVkIHVzZSBvbmx5MUUwQwYDVQQDEzxWZXJpU2lnbiBDbGFzcyAzIFB1YmxpYyBQcmlt
36265 YXJ5IENlcnRpZmljYXRpb24gQXV0aG9yaXR5IC0gRzUwggEiMA0GCSqGSIb3DQEBAQUAA4IBDwAw
36266 ggEKAoIBAQCvJAgIKXo1nmAMqudLO07cfLw8RRy7K+D+KQL5VwijZIUVJ/XxrcgxiV0i6CqqpkKz
36267 j/i5Vbext0uz/o9+B1fs70PbZmIVYc9gDaTY3vjgw2IIPVQT60nKWVSFJuUrjxuf6/WhkcIzSdhD
36268 Y2pSS9KP6HBRTdGJaXvHcPaz3BJ023tdS1bTlr8Vd6Gw9KIl8q8ckmcY5fQGBO+QueQA5N06tRn/
36269 Arr0PO7gi+s3i+z016zy9vA9r911kTMZHRxAy3QkGSGT2RT+rCpSx4/VBEnkjWNHiDxpg8v+R70r
36270 fk/Fla4OndTRQ8Bnc+MUCH7lP59zuDMKz10/NIeWiu5T6CUVAgMBAAGjgbIwga8wDwYDVR0TAQH/
36271 BAUwAwEB/zAOBgNVHQ8BAf8EBAMCAQYwbQYIKwYBBQUHAQwEYTBfoV2gWzBZMFcwVRYJaW1hZ2Uv
36272 Z2lmMCEwHzAHBgUrDgMCGgQUj+XTGoasjY5rw8+AatRIGCx7GS4wJRYjaHR0cDovL2xvZ28udmVy
36273 aXNpZ24uY29tL3ZzbG9nby5naWYwHQYDVR0OBBYEFH/TZafC3ey78DAJ80M5+gKvMzEzMA0GCSqG
36274 SIb3DQEBBQUAA4IBAQCTJEowX2LP2BqYLz3q3JktvXf2pXkiOOzEp6B4Eq1iDkVwZMXnl2YtmAl+
36275 X6/WzChl8gGqCBpH3vn5fJJaCGkgDdk+bW48DW7Y5gaRQBi5+MHt39tBquCWIMnNZBU4gcmU7qKE
36276 KQsTb47bDN0lAtukixlE0kF6BWlKWE9gyn6CagsCqiUXObXbf+eEZSqVir2G3l6BFoMtEMze/aiC
36277 Km0oHw0LxOXnGiYZ4fQRbxC1lfznQgUy286dUV4otp6F01vvpX1FQHKOtw5rDgb7MzVIcbidJ4vE
36278 ZV8NhnacRHr2lVz2XTIIM6RUthg/aFzyQkqFOFSDX9HoLPKsEdao7WNq
36279 -----END CERTIFICATE-----
36280
36281 SecureTrust CA
36282 ==============
36283 -----BEGIN CERTIFICATE-----
36284 MIIDuDCCAqCgAwIBAgIQDPCOXAgWpa1Cf/DrJxhZ0DANBgkqhkiG9w0BAQUFADBIMQswCQYDVQQG
36285 EwJVUzEgMB4GA1UEChMXU2VjdXJlVHJ1c3QgQ29ycG9yYXRpb24xFzAVBgNVBAMTDlNlY3VyZVRy
36286 dXN0IENBMB4XDTA2MTEwNzE5MzExOFoXDTI5MTIzMTE5NDA1NVowSDELMAkGA1UEBhMCVVMxIDAe
36287 BgNVBAoTF1NlY3VyZVRydXN0IENvcnBvcmF0aW9uMRcwFQYDVQQDEw5TZWN1cmVUcnVzdCBDQTCC
36288 ASIwDQYJKoZIhvcNAQEBBQADggEPADCCAQoCggEBAKukgeWVzfX2FI7CT8rU4niVWJxB4Q2ZQCQX
36289 OZEzZum+4YOvYlyJ0fwkW2Gz4BERQRwdbvC4u/jep4G6pkjGnx29vo6pQT64lO0pGtSO0gMdA+9t
36290 DWccV9cGrcrI9f4Or2YlSASWC12juhbDCE/RRvgUXPLIXgGZbf2IzIaowW8xQmxSPmjL8xk037uH
36291 GFaAJsTQ3MBv396gwpEWoGQRS0S8Hvbn+mPeZqx2pHGj7DaUaHp3pLHnDi+BeuK1cobvomuL8A/b
36292 01k/unK8RCSc43Oz969XL0Imnal0ugBS8kvNU3xHCzaFDmapCJcWNFfBZveA4+1wVMeT4C4oFVmH
36293 ursCAwEAAaOBnTCBmjATBgkrBgEEAYI3FAIEBh4EAEMAQTALBgNVHQ8EBAMCAYYwDwYDVR0TAQH/
36294 BAUwAwEB/zAdBgNVHQ4EFgQUQjK2FvoE/f5dS3rD/fdMQB1aQ68wNAYDVR0fBC0wKzApoCegJYYj
36295 aHR0cDovL2NybC5zZWN1cmV0cnVzdC5jb20vU1RDQS5jcmwwEAYJKwYBBAGCNxUBBAMCAQAwDQYJ
36296 KoZIhvcNAQEFBQADggEBADDtT0rhWDpSclu1pqNlGKa7UTt36Z3q059c4EVlew3KW+JwULKUBRSu
36297 SceNQQcSc5R+DCMh/bwQf2AQWnL1mA6s7Ll/3XpvXdMc9P+IBWlCqQVxyLesJugutIxq/3HcuLHf
36298 mbx8IVQr5Fiiu1cprp6poxkmD5kuCLDv/WnPmRoJjeOnnyvJNjR7JLN4TJUXpAYmHrZkUjZfYGfZ
36299 nMUFdAvnZyPSCPyI6a6Lf+Ew9Dd+/cYy2i2eRDAwbO4H3tI0/NL/QPZL9GZGBlSm8jIKYyYwa5vR
36300 3ItHuuG51WLQoqD0ZwV4KWMabwTW+MZMo5qxN7SN5ShLHZ4swrhovO0C7jE=
36301 -----END CERTIFICATE-----
36302
36303 Secure Global CA
36304 ================
36305 -----BEGIN CERTIFICATE-----
36306 MIIDvDCCAqSgAwIBAgIQB1YipOjUiolN9BPI8PjqpTANBgkqhkiG9w0BAQUFADBKMQswCQYDVQQG
36307 EwJVUzEgMB4GA1UEChMXU2VjdXJlVHJ1c3QgQ29ycG9yYXRpb24xGTAXBgNVBAMTEFNlY3VyZSBH
36308 bG9iYWwgQ0EwHhcNMDYxMTA3MTk0MjI4WhcNMjkxMjMxMTk1MjA2WjBKMQswCQYDVQQGEwJVUzEg
36309 MB4GA1UEChMXU2VjdXJlVHJ1c3QgQ29ycG9yYXRpb24xGTAXBgNVBAMTEFNlY3VyZSBHbG9iYWwg
36310 Q0EwggEiMA0GCSqGSIb3DQEBAQUAA4IBDwAwggEKAoIBAQCvNS7YrGxVaQZx5RNoJLNP2MwhR/jx
36311 YDiJiQPpvepeRlMJ3Fz1Wuj3RSoC6zFh1ykzTM7HfAo3fg+6MpjhHZevj8fcyTiW89sa/FHtaMbQ
36312 bqR8JNGuQsiWUGMu4P51/pinX0kuleM5M2SOHqRfkNJnPLLZ/kG5VacJjnIFHovdRIWCQtBJwB1g
36313 8NEXLJXr9qXBkqPFwqcIYA1gBBCWeZ4WNOaptvolRTnIHmX5k/Wq8VLcmZg9pYYaDDUz+kulBAYV
36314 HDGA76oYa8J719rO+TMg1fW9ajMtgQT7sFzUnKPiXB3jqUJ1XnvUd+85VLrJChgbEplJL4hL/VBi
36315 0XPnj3pDAgMBAAGjgZ0wgZowEwYJKwYBBAGCNxQCBAYeBABDAEEwCwYDVR0PBAQDAgGGMA8GA1Ud
36316 EwEB/wQFMAMBAf8wHQYDVR0OBBYEFK9EBMJBfkiD2045AuzshHrmzsmkMDQGA1UdHwQtMCswKaAn
36317 oCWGI2h0dHA6Ly9jcmwuc2VjdXJldHJ1c3QuY29tL1NHQ0EuY3JsMBAGCSsGAQQBgjcVAQQDAgEA
36318 MA0GCSqGSIb3DQEBBQUAA4IBAQBjGghAfaReUw132HquHw0LURYD7xh8yOOvaliTFGCRsoTciE6+
36319 OYo68+aCiV0BN7OrJKQVDpI1WkpEXk5X+nXOH0jOZvQ8QCaSmGwb7iRGDBezUqXbpZGRzzfTb+cn
36320 CDpOGR86p1hcF895P4vkp9MmI50mD1hp/Ed+stCNi5O/KU9DaXR2Z0vPB4zmAve14bRDtUstFJ/5
36321 3CYNv6ZHdAbYiNE6KTCEztI5gGIbqMdXSbxqVVFnFUq+NQfk1XWYN3kwFNspnWzFacxHVaIw98xc
36322 f8LDmBxrThaA63p4ZUWiABqvDA1VZDRIuJK58bRQKfJPIx/abKwfROHdI3hRW8cW
36323 -----END CERTIFICATE-----
36324
36325 COMODO Certification Authority
36326 ==============================
36327 -----BEGIN CERTIFICATE-----
36328 MIIEHTCCAwWgAwIBAgIQToEtioJl4AsC7j41AkblPTANBgkqhkiG9w0BAQUFADCBgTELMAkGA1UE
36329 BhMCR0IxGzAZBgNVBAgTEkdyZWF0ZXIgTWFuY2hlc3RlcjEQMA4GA1UEBxMHU2FsZm9yZDEaMBgG
36330 A1UEChMRQ09NT0RPIENBIExpbWl0ZWQxJzAlBgNVBAMTHkNPTU9ETyBDZXJ0aWZpY2F0aW9uIEF1
36331 dGhvcml0eTAeFw0wNjEyMDEwMDAwMDBaFw0yOTEyMzEyMzU5NTlaMIGBMQswCQYDVQQGEwJHQjEb
36332 MBkGA1UECBMSR3JlYXRlciBNYW5jaGVzdGVyMRAwDgYDVQQHEwdTYWxmb3JkMRowGAYDVQQKExFD
36333 T01PRE8gQ0EgTGltaXRlZDEnMCUGA1UEAxMeQ09NT0RPIENlcnRpZmljYXRpb24gQXV0aG9yaXR5
36334 MIIBIjANBgkqhkiG9w0BAQEFAAOCAQ8AMIIBCgKCAQEA0ECLi3LjkRv3UcEbVASY06m/weaKXTuH
36335 +7uIzg3jLz8GlvCiKVCZrts7oVewdFFxze1CkU1B/qnI2GqGd0S7WWaXUF601CxwRM/aN5VCaTww
36336 xHGzUvAhTaHYujl8HJ6jJJ3ygxaYqhZ8Q5sVW7euNJH+1GImGEaaP+vB+fGQV+useg2L23IwambV
36337 4EajcNxo2f8ESIl33rXp+2dtQem8Ob0y2WIC8bGoPW43nOIv4tOiJovGuFVDiOEjPqXSJDlqR6sA
36338 1KGzqSX+DT+nHbrTUcELpNqsOO9VUCQFZUaTNE8tja3G1CEZ0o7KBWFxB3NH5YoZEr0ETc5OnKVI
36339 rLsm9wIDAQABo4GOMIGLMB0GA1UdDgQWBBQLWOWLxkwVN6RAqTCpIb5HNlpW/zAOBgNVHQ8BAf8E
36340 BAMCAQYwDwYDVR0TAQH/BAUwAwEB/zBJBgNVHR8EQjBAMD6gPKA6hjhodHRwOi8vY3JsLmNvbW9k
36341 b2NhLmNvbS9DT01PRE9DZXJ0aWZpY2F0aW9uQXV0aG9yaXR5LmNybDANBgkqhkiG9w0BAQUFAAOC
36342 AQEAPpiem/Yb6dc5t3iuHXIYSdOH5EOC6z/JqvWote9VfCFSZfnVDeFs9D6Mk3ORLgLETgdxb8CP
36343 OGEIqB6BCsAvIC9Bi5HcSEW88cbeunZrM8gALTFGTO3nnc+IlP8zwFboJIYmuNg4ON8qa90SzMc/
36344 RxdMosIGlgnW2/4/PEZB31jiVg88O8EckzXZOFKs7sjsLjBOlDW0JB9LeGna8gI4zJVSk/BwJVmc
36345 IGfE7vmLV2H0knZ9P4SNVbfo5azV8fUZVqZa+5Acr5Pr5RzUZ5ddBA6+C4OmF4O5MBKgxTMVBbkN
36346 +8cFduPYSo38NBejxiEovjBFMR7HeL5YYTisO+IBZQ==
36347 -----END CERTIFICATE-----
36348
36349 Network Solutions Certificate Authority
36350 =======================================
36351 -----BEGIN CERTIFICATE-----
36352 MIID5jCCAs6gAwIBAgIQV8szb8JcFuZHFhfjkDFo4DANBgkqhkiG9w0BAQUFADBiMQswCQYDVQQG
36353 EwJVUzEhMB8GA1UEChMYTmV0d29yayBTb2x1dGlvbnMgTC5MLkMuMTAwLgYDVQQDEydOZXR3b3Jr
36354 IFNvbHV0aW9ucyBDZXJ0aWZpY2F0ZSBBdXRob3JpdHkwHhcNMDYxMjAxMDAwMDAwWhcNMjkxMjMx
36355 MjM1OTU5WjBiMQswCQYDVQQGEwJVUzEhMB8GA1UEChMYTmV0d29yayBTb2x1dGlvbnMgTC5MLkMu
36356 MTAwLgYDVQQDEydOZXR3b3JrIFNvbHV0aW9ucyBDZXJ0aWZpY2F0ZSBBdXRob3JpdHkwggEiMA0G
36357 CSqGSIb3DQEBAQUAA4IBDwAwggEKAoIBAQDkvH6SMG3G2I4rC7xGzuAnlt7e+foS0zwzc7MEL7xx
36358 jOWftiJgPl9dzgn/ggwbmlFQGiaJ3dVhXRncEg8tCqJDXRfQNJIg6nPPOCwGJgl6cvf6UDL4wpPT
36359 aaIjzkGxzOTVHzbRijr4jGPiFFlp7Q3Tf2vouAPlT2rlmGNpSAW+Lv8ztumXWWn4Zxmuk2GWRBXT
36360 crA/vGp97Eh/jcOrqnErU2lBUzS1sLnFBgrEsEX1QV1uiUV7PTsmjHTC5dLRfbIR1PtYMiKagMnc
36361 /Qzpf14Dl847ABSHJ3A4qY5usyd2mFHgBeMhqxrVhSI8KbWaFsWAqPS7azCPL0YCorEMIuDTAgMB
36362 AAGjgZcwgZQwHQYDVR0OBBYEFCEwyfsA106Y2oeqKtCnLrFAMadMMA4GA1UdDwEB/wQEAwIBBjAP
36363 BgNVHRMBAf8EBTADAQH/MFIGA1UdHwRLMEkwR6BFoEOGQWh0dHA6Ly9jcmwubmV0c29sc3NsLmNv
36364 bS9OZXR3b3JrU29sdXRpb25zQ2VydGlmaWNhdGVBdXRob3JpdHkuY3JsMA0GCSqGSIb3DQEBBQUA
36365 A4IBAQC7rkvnt1frf6ott3NHhWrB5KUd5Oc86fRZZXe1eltajSU24HqXLjjAV2CDmAaDn7l2em5Q
36366 4LqILPxFzBiwmZVRDuwduIj/h1AcgsLj4DKAv6ALR8jDMe+ZZzKATxcheQxpXN5eNK4CtSbqUN9/
36367 GGUsyfJj4akH/nxxH2szJGoeBfcFaMBqEssuXmHLrijTfsK0ZpEmXzwuJF/LWA/rKOyvEZbz3Htv
36368 wKeI8lN3s2Berq4o2jUsbzRF0ybh3uxbTydrFny9RAQYgrOJeRcQcT16ohZO9QHNpGxlaKFJdlxD
36369 ydi8NmdspZS11My5vWo1ViHe2MPr+8ukYEywVaCge1ey
36370 -----END CERTIFICATE-----
36371
36372 WellsSecure Public Root Certificate Authority
36373 =============================================
36374 -----BEGIN CERTIFICATE-----
36375 MIIEvTCCA6WgAwIBAgIBATANBgkqhkiG9w0BAQUFADCBhTELMAkGA1UEBhMCVVMxIDAeBgNVBAoM
36376 F1dlbGxzIEZhcmdvIFdlbGxzU2VjdXJlMRwwGgYDVQQLDBNXZWxscyBGYXJnbyBCYW5rIE5BMTYw
36377 NAYDVQQDDC1XZWxsc1NlY3VyZSBQdWJsaWMgUm9vdCBDZXJ0aWZpY2F0ZSBBdXRob3JpdHkwHhcN
36378 MDcxMjEzMTcwNzU0WhcNMjIxMjE0MDAwNzU0WjCBhTELMAkGA1UEBhMCVVMxIDAeBgNVBAoMF1dl
36379 bGxzIEZhcmdvIFdlbGxzU2VjdXJlMRwwGgYDVQQLDBNXZWxscyBGYXJnbyBCYW5rIE5BMTYwNAYD
36380 VQQDDC1XZWxsc1NlY3VyZSBQdWJsaWMgUm9vdCBDZXJ0aWZpY2F0ZSBBdXRob3JpdHkwggEiMA0G
36381 CSqGSIb3DQEBAQUAA4IBDwAwggEKAoIBAQDub7S9eeKPCCGeOARBJe+rWxxTkqxtnt3CxC5FlAM1
36382 iGd0V+PfjLindo8796jE2yljDpFoNoqXjopxaAkH5OjUDk/41itMpBb570OYj7OeUt9tkTmPOL13
36383 i0Nj67eT/DBMHAGTthP796EfvyXhdDcsHqRePGj4S78NuR4uNuip5Kf4D8uCdXw1LSLWwr8L87T8
36384 bJVhHlfXBIEyg1J55oNjz7fLY4sR4r1e6/aN7ZVyKLSsEmLpSjPmgzKuBXWVvYSV2ypcm44uDLiB
36385 K0HmOFafSZtsdvqKXfcBeYF8wYNABf5x/Qw/zE5gCQ5lRxAvAcAFP4/4s0HvWkJ+We/SlwxlAgMB
36386 AAGjggE0MIIBMDAPBgNVHRMBAf8EBTADAQH/MDkGA1UdHwQyMDAwLqAsoCqGKGh0dHA6Ly9jcmwu
36387 cGtpLndlbGxzZmFyZ28uY29tL3dzcHJjYS5jcmwwDgYDVR0PAQH/BAQDAgHGMB0GA1UdDgQWBBQm
36388 lRkQ2eihl5H/3BnZtQQ+0nMKajCBsgYDVR0jBIGqMIGngBQmlRkQ2eihl5H/3BnZtQQ+0nMKaqGB
36389 i6SBiDCBhTELMAkGA1UEBhMCVVMxIDAeBgNVBAoMF1dlbGxzIEZhcmdvIFdlbGxzU2VjdXJlMRww
36390 GgYDVQQLDBNXZWxscyBGYXJnbyBCYW5rIE5BMTYwNAYDVQQDDC1XZWxsc1NlY3VyZSBQdWJsaWMg
36391 Um9vdCBDZXJ0aWZpY2F0ZSBBdXRob3JpdHmCAQEwDQYJKoZIhvcNAQEFBQADggEBALkVsUSRzCPI
36392 K0134/iaeycNzXK7mQDKfGYZUMbVmO2rvwNa5U3lHshPcZeG1eMd/ZDJPHV3V3p9+N701NX3leZ0
36393 bh08rnyd2wIDBSxxSyU+B+NemvVmFymIGjifz6pBA4SXa5M4esowRBskRDPQ5NHcKDj0E0M1NSlj
36394 qHyita04pO2t/caaH/+Xc/77szWnk4bGdpEA5qxRFsQnMlzbc9qlk1eOPm01JghZ1edE13YgY+es
36395 E2fDbbFwRnzVlhE9iW9dqKHrjQrawx0zbKPqZxmamX9LPYNRKh3KL4YMon4QLSvUFpULB6ouFJJJ
36396 tylv2G0xffX8oRAHh84vWdw+WNs=
36397 -----END CERTIFICATE-----
36398
36399 COMODO ECC Certification Authority
36400 ==================================
36401 -----BEGIN CERTIFICATE-----
36402 MIICiTCCAg+gAwIBAgIQH0evqmIAcFBUTAGem2OZKjAKBggqhkjOPQQDAzCBhTELMAkGA1UEBhMC
36403 R0IxGzAZBgNVBAgTEkdyZWF0ZXIgTWFuY2hlc3RlcjEQMA4GA1UEBxMHU2FsZm9yZDEaMBgGA1UE
36404 ChMRQ09NT0RPIENBIExpbWl0ZWQxKzApBgNVBAMTIkNPTU9ETyBFQ0MgQ2VydGlmaWNhdGlvbiBB
36405 dXRob3JpdHkwHhcNMDgwMzA2MDAwMDAwWhcNMzgwMTE4MjM1OTU5WjCBhTELMAkGA1UEBhMCR0Ix
36406 GzAZBgNVBAgTEkdyZWF0ZXIgTWFuY2hlc3RlcjEQMA4GA1UEBxMHU2FsZm9yZDEaMBgGA1UEChMR
36407 Q09NT0RPIENBIExpbWl0ZWQxKzApBgNVBAMTIkNPTU9ETyBFQ0MgQ2VydGlmaWNhdGlvbiBBdXRo
36408 b3JpdHkwdjAQBgcqhkjOPQIBBgUrgQQAIgNiAAQDR3svdcmCFYX7deSRFtSrYpn1PlILBs5BAH+X
36409 4QokPB0BBO490o0JlwzgdeT6+3eKKvUDYEs2ixYjFq0JcfRK9ChQtP6IHG4/bC8vCVlbpVsLM5ni
36410 wz2J+Wos77LTBumjQjBAMB0GA1UdDgQWBBR1cacZSBm8nZ3qQUfflMRId5nTeTAOBgNVHQ8BAf8E
36411 BAMCAQYwDwYDVR0TAQH/BAUwAwEB/zAKBggqhkjOPQQDAwNoADBlAjEA7wNbeqy3eApyt4jf/7VG
36412 FAkK+qDmfQjGGoe9GKhzvSbKYAydzpmfz1wPMOG+FDHqAjAU9JM8SaczepBGR7NjfRObTrdvGDeA
36413 U/7dIOA1mjbRxwG55tzd8/8dLDoWV9mSOdY=
36414 -----END CERTIFICATE-----
36415
36416 IGC/A
36417 =====
36418 -----BEGIN CERTIFICATE-----
36419 MIIEAjCCAuqgAwIBAgIFORFFEJQwDQYJKoZIhvcNAQEFBQAwgYUxCzAJBgNVBAYTAkZSMQ8wDQYD
36420 VQQIEwZGcmFuY2UxDjAMBgNVBAcTBVBhcmlzMRAwDgYDVQQKEwdQTS9TR0ROMQ4wDAYDVQQLEwVE
36421 Q1NTSTEOMAwGA1UEAxMFSUdDL0ExIzAhBgkqhkiG9w0BCQEWFGlnY2FAc2dkbi5wbS5nb3V2LmZy
36422 MB4XDTAyMTIxMzE0MjkyM1oXDTIwMTAxNzE0MjkyMlowgYUxCzAJBgNVBAYTAkZSMQ8wDQYDVQQI
36423 EwZGcmFuY2UxDjAMBgNVBAcTBVBhcmlzMRAwDgYDVQQKEwdQTS9TR0ROMQ4wDAYDVQQLEwVEQ1NT
36424 STEOMAwGA1UEAxMFSUdDL0ExIzAhBgkqhkiG9w0BCQEWFGlnY2FAc2dkbi5wbS5nb3V2LmZyMIIB
36425 IjANBgkqhkiG9w0BAQEFAAOCAQ8AMIIBCgKCAQEAsh/R0GLFMzvABIaIs9z4iPf930Pfeo2aSVz2
36426 TqrMHLmh6yeJ8kbpO0px1R2OLc/mratjUMdUC24SyZA2xtgv2pGqaMVy/hcKshd+ebUyiHDKcMCW
36427 So7kVc0dJ5S/znIq7Fz5cyD+vfcuiWe4u0dzEvfRNWk68gq5rv9GQkaiv6GFGvm/5P9JhfejcIYy
36428 HF2fYPepraX/z9E0+X1bF8bc1g4oa8Ld8fUzaJ1O/Id8NhLWo4DoQw1VYZTqZDdH6nfK0LJYBcNd
36429 frGoRpAxVs5wKpayMLh35nnAvSk7/ZR3TL0gzUEl4C7HG7vupARB0l2tEmqKm0f7yd1GQOGdPDPQ
36430 tQIDAQABo3cwdTAPBgNVHRMBAf8EBTADAQH/MAsGA1UdDwQEAwIBRjAVBgNVHSAEDjAMMAoGCCqB
36431 egF5AQEBMB0GA1UdDgQWBBSjBS8YYFDCiQrdKyFP/45OqDAxNjAfBgNVHSMEGDAWgBSjBS8YYFDC
36432 iQrdKyFP/45OqDAxNjANBgkqhkiG9w0BAQUFAAOCAQEABdwm2Pp3FURo/C9mOnTgXeQp/wYHE4RK
36433 q89toB9RlPhJy3Q2FLwV3duJL92PoF189RLrn544pEfMs5bZvpwlqwN+Mw+VgQ39FuCIvjfwbF3Q
36434 MZsyK10XZZOYYLxuj7GoPB7ZHPOpJkL5ZB3C55L29B5aqhlSXa/oovdgoPaN8In1buAKBQGVyYsg
36435 Crpa/JosPL3Dt8ldeCUFP1YUmwza+zpI/pdpXsoQhvdOlgQITeywvl3cO45Pwf2aNjSaTFR+FwNI
36436 lQgRHAdvhQh+XU3Endv7rs6y0bO4g2wdsrN58dhwmX7wEwLOXt1R0982gaEbeC9xs/FZTEYYKKuF
36437 0mBWWg==
36438 -----END CERTIFICATE-----
36439
36440 Security Communication EV RootCA1
36441 =================================
36442 -----BEGIN CERTIFICATE-----
36443 MIIDfTCCAmWgAwIBAgIBADANBgkqhkiG9w0BAQUFADBgMQswCQYDVQQGEwJKUDElMCMGA1UEChMc
36444 U0VDT00gVHJ1c3QgU3lzdGVtcyBDTy4sTFRELjEqMCgGA1UECxMhU2VjdXJpdHkgQ29tbXVuaWNh
36445 dGlvbiBFViBSb290Q0ExMB4XDTA3MDYwNjAyMTIzMloXDTM3MDYwNjAyMTIzMlowYDELMAkGA1UE
36446 BhMCSlAxJTAjBgNVBAoTHFNFQ09NIFRydXN0IFN5c3RlbXMgQ08uLExURC4xKjAoBgNVBAsTIVNl
36447 Y3VyaXR5IENvbW11bmljYXRpb24gRVYgUm9vdENBMTCCASIwDQYJKoZIhvcNAQEBBQADggEPADCC
36448 AQoCggEBALx/7FebJOD+nLpCeamIivqA4PUHKUPqjgo0No0c+qe1OXj/l3X3L+SqawSERMqm4miO
36449 /VVQYg+kcQ7OBzgtQoVQrTyWb4vVog7P3kmJPdZkLjjlHmy1V4qe70gOzXppFodEtZDkBp2uoQSX
36450 WHnvIEqCa4wiv+wfD+mEce3xDuS4GBPMVjZd0ZoeUWs5bmB2iDQL87PRsJ3KYeJkHcFGB7hj3R4z
36451 ZbOOCVVSPbW9/wfrrWFVGCypaZhKqkDFMxRldAD5kd6vA0jFQFTcD4SQaCDFkpbcLuUCRarAX1T4
36452 bepJz11sS6/vmsJWXMY1VkJqMF/Cq/biPT+zyRGPMUzXn0kCAwEAAaNCMEAwHQYDVR0OBBYEFDVK
36453 9U2vP9eCOKyrcWUXdYydVZPmMA4GA1UdDwEB/wQEAwIBBjAPBgNVHRMBAf8EBTADAQH/MA0GCSqG
36454 SIb3DQEBBQUAA4IBAQCoh+ns+EBnXcPBZsdAS5f8hxOQWsTvoMpfi7ent/HWtWS3irO4G8za+6xm
36455 iEHO6Pzk2x6Ipu0nUBsCMCRGef4Eh3CXQHPRwMFXGZpppSeZq51ihPZRwSzJIxXYKLerJRO1RuGG
36456 Av8mjMSIkh1W/hln8lXkgKNrnKt34VFxDSDbEJrbvXZ5B3eZKK2aXtqxT0QsNY6llsf9g/BYxnnW
36457 mHyojf6GPgcWkuF75x3sM3Z+Qi5KhfmRiWiEA4Glm5q+4zfFVKtWOxgtQaQM+ELbmaDgcm+7XeEW
36458 T1MKZPlO9L9OVL14bIjqv5wTJMJwaaJ/D8g8rQjJsJhAoyrniIPtd490
36459 -----END CERTIFICATE-----
36460
36461 OISTE WISeKey Global Root GA CA
36462 ===============================
36463 -----BEGIN CERTIFICATE-----
36464 MIID8TCCAtmgAwIBAgIQQT1yx/RrH4FDffHSKFTfmjANBgkqhkiG9w0BAQUFADCBijELMAkGA1UE
36465 BhMCQ0gxEDAOBgNVBAoTB1dJU2VLZXkxGzAZBgNVBAsTEkNvcHlyaWdodCAoYykgMjAwNTEiMCAG
36466 A1UECxMZT0lTVEUgRm91bmRhdGlvbiBFbmRvcnNlZDEoMCYGA1UEAxMfT0lTVEUgV0lTZUtleSBH
36467 bG9iYWwgUm9vdCBHQSBDQTAeFw0wNTEyMTExNjAzNDRaFw0zNzEyMTExNjA5NTFaMIGKMQswCQYD
36468 VQQGEwJDSDEQMA4GA1UEChMHV0lTZUtleTEbMBkGA1UECxMSQ29weXJpZ2h0IChjKSAyMDA1MSIw
36469 IAYDVQQLExlPSVNURSBGb3VuZGF0aW9uIEVuZG9yc2VkMSgwJgYDVQQDEx9PSVNURSBXSVNlS2V5
36470 IEdsb2JhbCBSb290IEdBIENBMIIBIjANBgkqhkiG9w0BAQEFAAOCAQ8AMIIBCgKCAQEAy0+zAJs9
36471 Nt350UlqaxBJH+zYK7LG+DKBKUOVTJoZIyEVRd7jyBxRVVuuk+g3/ytr6dTqvirdqFEr12bDYVxg
36472 Asj1znJ7O7jyTmUIms2kahnBAbtzptf2w93NvKSLtZlhuAGio9RN1AU9ka34tAhxZK9w8RxrfvbD
36473 d50kc3vkDIzh2TbhmYsFmQvtRTEJysIA2/dyoJaqlYfQjse2YXMNdmaM3Bu0Y6Kff5MTMPGhJ9vZ
36474 /yxViJGg4E8HsChWjBgbl0SOid3gF27nKu+POQoxhILYQBRJLnpB5Kf+42TMwVlxSywhp1t94B3R
36475 LoGbw9ho972WG6xwsRYUC9tguSYBBQIDAQABo1EwTzALBgNVHQ8EBAMCAYYwDwYDVR0TAQH/BAUw
36476 AwEB/zAdBgNVHQ4EFgQUswN+rja8sHnR3JQmthG+IbJphpQwEAYJKwYBBAGCNxUBBAMCAQAwDQYJ
36477 KoZIhvcNAQEFBQADggEBAEuh/wuHbrP5wUOxSPMowB0uyQlB+pQAHKSkq0lPjz0e701vvbyk9vIm
36478 MMkQyh2I+3QZH4VFvbBsUfk2ftv1TDI6QU9bR8/oCy22xBmddMVHxjtqD6wU2zz0c5ypBd8A3HR4
36479 +vg1YFkCExh8vPtNsCBtQ7tgMHpnM1zFmdH4LTlSc/uMqpclXHLZCB6rTjzjgTGfA6b7wP4piFXa
36480 hNVQA7bihKOmNqoROgHhGEvWRGizPflTdISzRpFGlgC3gCy24eMQ4tui5yiPAZZiFj4A4xylNoEY
36481 okxSdsARo27mHbrjWr42U8U+dY+GaSlYU7Wcu2+fXMUY7N0v4ZjJ/L7fCg0=
36482 -----END CERTIFICATE-----
36483
36484 S-TRUST Authentication and Encryption Root CA 2005 PN
36485 =====================================================
36486 -----BEGIN CERTIFICATE-----
36487 MIIEezCCA2OgAwIBAgIQNxkY5lNUfBq1uMtZWts1tzANBgkqhkiG9w0BAQUFADCBrjELMAkGA1UE
36488 BhMCREUxIDAeBgNVBAgTF0JhZGVuLVd1ZXJ0dGVtYmVyZyAoQlcpMRIwEAYDVQQHEwlTdHV0dGdh
36489 cnQxKTAnBgNVBAoTIERldXRzY2hlciBTcGFya2Fzc2VuIFZlcmxhZyBHbWJIMT4wPAYDVQQDEzVT
36490 LVRSVVNUIEF1dGhlbnRpY2F0aW9uIGFuZCBFbmNyeXB0aW9uIFJvb3QgQ0EgMjAwNTpQTjAeFw0w
36491 NTA2MjIwMDAwMDBaFw0zMDA2MjEyMzU5NTlaMIGuMQswCQYDVQQGEwJERTEgMB4GA1UECBMXQmFk
36492 ZW4tV3VlcnR0ZW1iZXJnIChCVykxEjAQBgNVBAcTCVN0dXR0Z2FydDEpMCcGA1UEChMgRGV1dHNj
36493 aGVyIFNwYXJrYXNzZW4gVmVybGFnIEdtYkgxPjA8BgNVBAMTNVMtVFJVU1QgQXV0aGVudGljYXRp
36494 b24gYW5kIEVuY3J5cHRpb24gUm9vdCBDQSAyMDA1OlBOMIIBIjANBgkqhkiG9w0BAQEFAAOCAQ8A
36495 MIIBCgKCAQEA2bVKwdMz6tNGs9HiTNL1toPQb9UY6ZOvJ44TzbUlNlA0EmQpoVXhOmCTnijJ4/Ob
36496 4QSwI7+Vio5bG0F/WsPoTUzVJBY+h0jUJ67m91MduwwA7z5hca2/OnpYH5Q9XIHV1W/fuJvS9eXL
36497 g3KSwlOyggLrra1fFi2SU3bxibYs9cEv4KdKb6AwajLrmnQDaHgTncovmwsdvs91DSaXm8f1Xgqf
36498 eN+zvOyauu9VjxuapgdjKRdZYgkqeQd3peDRF2npW932kKvimAoA0SVtnteFhy+S8dF2g08LOlk3
36499 KC8zpxdQ1iALCvQm+Z845y2kuJuJja2tyWp9iRe79n+Ag3rm7QIDAQABo4GSMIGPMBIGA1UdEwEB
36500 /wQIMAYBAf8CAQAwDgYDVR0PAQH/BAQDAgEGMCkGA1UdEQQiMCCkHjAcMRowGAYDVQQDExFTVFJv
36501 bmxpbmUxLTIwNDgtNTAdBgNVHQ4EFgQUD8oeXHngovMpttKFswtKtWXsa1IwHwYDVR0jBBgwFoAU
36502 D8oeXHngovMpttKFswtKtWXsa1IwDQYJKoZIhvcNAQEFBQADggEBAK8B8O0ZPCjoTVy7pWMciDMD
36503 pwCHpB8gq9Yc4wYfl35UvbfRssnV2oDsF9eK9XvCAPbpEW+EoFolMeKJ+aQAPzFoLtU96G7m1R08
36504 P7K9n3frndOMusDXtk3sU5wPBG7qNWdX4wple5A64U8+wwCSersFiXOMy6ZNwPv2AtawB6MDwidA
36505 nwzkhYItr5pCHdDHjfhA7p0GVxzZotiAFP7hYy0yh9WUUpY6RsZxlj33mA6ykaqP2vROJAA5Veit
36506 F7nTNCtKqUDMFypVZUF0Qn71wK/Ik63yGFs9iQzbRzkk+OBM8h+wPQrKBU6JIRrjKpms/H+h8Q8b
36507 Hz2eBIPdltkdOpQ=
36508 -----END CERTIFICATE-----
36509
36510 Microsec e-Szigno Root CA
36511 =========================
36512 -----BEGIN CERTIFICATE-----
36513 MIIHqDCCBpCgAwIBAgIRAMy4579OKRr9otxmpRwsDxEwDQYJKoZIhvcNAQEFBQAwcjELMAkGA1UE
36514 BhMCSFUxETAPBgNVBAcTCEJ1ZGFwZXN0MRYwFAYDVQQKEw1NaWNyb3NlYyBMdGQuMRQwEgYDVQQL
36515 EwtlLVN6aWdubyBDQTEiMCAGA1UEAxMZTWljcm9zZWMgZS1Temlnbm8gUm9vdCBDQTAeFw0wNTA0
36516 MDYxMjI4NDRaFw0xNzA0MDYxMjI4NDRaMHIxCzAJBgNVBAYTAkhVMREwDwYDVQQHEwhCdWRhcGVz
36517 dDEWMBQGA1UEChMNTWljcm9zZWMgTHRkLjEUMBIGA1UECxMLZS1Temlnbm8gQ0ExIjAgBgNVBAMT
36518 GU1pY3Jvc2VjIGUtU3ppZ25vIFJvb3QgQ0EwggEiMA0GCSqGSIb3DQEBAQUAA4IBDwAwggEKAoIB
36519 AQDtyADVgXvNOABHzNuEwSFpLHSQDCHZU4ftPkNEU6+r+ICbPHiN1I2uuO/TEdyB5s87lozWbxXG
36520 d36hL+BfkrYn13aaHUM86tnsL+4582pnS4uCzyL4ZVX+LMsvfUh6PXX5qqAnu3jCBspRwn5mS6/N
36521 oqdNAoI/gqyFxuEPkEeZlApxcpMqyabAvjxWTHOSJ/FrtfX9/DAFYJLG65Z+AZHCabEeHXtTRbjc
36522 QR/Ji3HWVBTji1R4P770Yjtb9aPs1ZJ04nQw7wHb4dSrmZsqa/i9phyGI0Jf7Enemotb9HI6QMVJ
36523 PqW+jqpx62z69Rrkav17fVVA71hu5tnVvCSrwe+3AgMBAAGjggQ3MIIEMzBnBggrBgEFBQcBAQRb
36524 MFkwKAYIKwYBBQUHMAGGHGh0dHBzOi8vcmNhLmUtc3ppZ25vLmh1L29jc3AwLQYIKwYBBQUHMAKG
36525 IWh0dHA6Ly93d3cuZS1zemlnbm8uaHUvUm9vdENBLmNydDAPBgNVHRMBAf8EBTADAQH/MIIBcwYD
36526 VR0gBIIBajCCAWYwggFiBgwrBgEEAYGoGAIBAQEwggFQMCgGCCsGAQUFBwIBFhxodHRwOi8vd3d3
36527 LmUtc3ppZ25vLmh1L1NaU1ovMIIBIgYIKwYBBQUHAgIwggEUHoIBEABBACAAdABhAG4A+gBzAO0A
36528 dAB2AOEAbgB5ACAA6QByAHQAZQBsAG0AZQB6AOkAcwDpAGgAZQB6ACAA6QBzACAAZQBsAGYAbwBn
36529 AGEAZADhAHMA4QBoAG8AegAgAGEAIABTAHoAbwBsAGcA4QBsAHQAYQB0APMAIABTAHoAbwBsAGcA
36530 4QBsAHQAYQB0AOEAcwBpACAAUwB6AGEAYgDhAGwAeQB6AGEAdABhACAAcwB6AGUAcgBpAG4AdAAg
36531 AGsAZQBsAGwAIABlAGwAagDhAHIAbgBpADoAIABoAHQAdABwADoALwAvAHcAdwB3AC4AZQAtAHMA
36532 egBpAGcAbgBvAC4AaAB1AC8AUwBaAFMAWgAvMIHIBgNVHR8EgcAwgb0wgbqggbeggbSGIWh0dHA6
36533 Ly93d3cuZS1zemlnbm8uaHUvUm9vdENBLmNybIaBjmxkYXA6Ly9sZGFwLmUtc3ppZ25vLmh1L0NO
36534 PU1pY3Jvc2VjJTIwZS1Temlnbm8lMjBSb290JTIwQ0EsT1U9ZS1Temlnbm8lMjBDQSxPPU1pY3Jv
36535 c2VjJTIwTHRkLixMPUJ1ZGFwZXN0LEM9SFU/Y2VydGlmaWNhdGVSZXZvY2F0aW9uTGlzdDtiaW5h
36536 cnkwDgYDVR0PAQH/BAQDAgEGMIGWBgNVHREEgY4wgYuBEGluZm9AZS1zemlnbm8uaHWkdzB1MSMw
36537 IQYDVQQDDBpNaWNyb3NlYyBlLVN6aWduw7MgUm9vdCBDQTEWMBQGA1UECwwNZS1TemlnbsOzIEhT
36538 WjEWMBQGA1UEChMNTWljcm9zZWMgS2Z0LjERMA8GA1UEBxMIQnVkYXBlc3QxCzAJBgNVBAYTAkhV
36539 MIGsBgNVHSMEgaQwgaGAFMegSXUWYYTbMUuE0vE3QJDvTtz3oXakdDByMQswCQYDVQQGEwJIVTER
36540 MA8GA1UEBxMIQnVkYXBlc3QxFjAUBgNVBAoTDU1pY3Jvc2VjIEx0ZC4xFDASBgNVBAsTC2UtU3pp
36541 Z25vIENBMSIwIAYDVQQDExlNaWNyb3NlYyBlLVN6aWdubyBSb290IENBghEAzLjnv04pGv2i3Gal
36542 HCwPETAdBgNVHQ4EFgQUx6BJdRZhhNsxS4TS8TdAkO9O3PcwDQYJKoZIhvcNAQEFBQADggEBANMT
36543 nGZjWS7KXHAM/IO8VbH0jgdsZifOwTsgqRy7RlRw7lrMoHfqaEQn6/Ip3Xep1fvj1KcExJW4C+FE
36544 aGAHQzAxQmHl7tnlJNUb3+FKG6qfx1/4ehHqE5MAyopYse7tDk2016g2JnzgOsHVV4Lxdbb9iV/a
36545 86g4nzUGCM4ilb7N1fy+W955a9x6qWVmvrElWl/tftOsRm1M9DKHtCAE4Gx4sHfRhUZLphK3dehK
36546 yVZs15KrnfVJONJPU+NVkBHbmJbGSfI+9J8b4PeI3CVimUTYc78/MPMMNz7UwiiAc7EBt51alhQB
36547 S6kRnSlqLtBdgcDPsiBDxwPgN05dCtxZICU=
36548 -----END CERTIFICATE-----
36549
36550 Certigna
36551 ========
36552 -----BEGIN CERTIFICATE-----
36553 MIIDqDCCApCgAwIBAgIJAP7c4wEPyUj/MA0GCSqGSIb3DQEBBQUAMDQxCzAJBgNVBAYTAkZSMRIw
36554 EAYDVQQKDAlEaGlteW90aXMxETAPBgNVBAMMCENlcnRpZ25hMB4XDTA3MDYyOTE1MTMwNVoXDTI3
36555 MDYyOTE1MTMwNVowNDELMAkGA1UEBhMCRlIxEjAQBgNVBAoMCURoaW15b3RpczERMA8GA1UEAwwI
36556 Q2VydGlnbmEwggEiMA0GCSqGSIb3DQEBAQUAA4IBDwAwggEKAoIBAQDIaPHJ1tazNHUmgh7stL7q
36557 XOEm7RFHYeGifBZ4QCHkYJ5ayGPhxLGWkv8YbWkj4Sti993iNi+RB7lIzw7sebYs5zRLcAglozyH
36558 GxnygQcPOJAZ0xH+hrTy0V4eHpbNgGzOOzGTtvKg0KmVEn2lmsxryIRWijOp5yIVUxbwzBfsV1/p
36559 ogqYCd7jX5xv3EjjhQsVWqa6n6xI4wmy9/Qy3l40vhx4XUJbzg4ij02Q130yGLMLLGq/jj8UEYkg
36560 DncUtT2UCIf3JR7VsmAA7G8qKCVuKj4YYxclPz5EIBb2JsglrgVKtOdjLPOMFlN+XPsRGgjBRmKf
36561 Irjxwo1p3Po6WAbfAgMBAAGjgbwwgbkwDwYDVR0TAQH/BAUwAwEB/zAdBgNVHQ4EFgQUGu3+QTmQ
36562 tCRZvgHyUtVF9lo53BEwZAYDVR0jBF0wW4AUGu3+QTmQtCRZvgHyUtVF9lo53BGhOKQ2MDQxCzAJ
36563 BgNVBAYTAkZSMRIwEAYDVQQKDAlEaGlteW90aXMxETAPBgNVBAMMCENlcnRpZ25hggkA/tzjAQ/J
36564 SP8wDgYDVR0PAQH/BAQDAgEGMBEGCWCGSAGG+EIBAQQEAwIABzANBgkqhkiG9w0BAQUFAAOCAQEA
36565 hQMeknH2Qq/ho2Ge6/PAD/Kl1NqV5ta+aDY9fm4fTIrv0Q8hbV6lUmPOEvjvKtpv6zf+EwLHyzs+
36566 ImvaYS5/1HI93TDhHkxAGYwP15zRgzB7mFncfca5DClMoTOi62c6ZYTTluLtdkVwj7Ur3vkj1klu
36567 PBS1xp81HlDQwY9qcEQCYsuuHWhBp6pX6FOqB9IG9tUUBguRA3UsbHK1YZWaDYu5Def131TN3ubY
36568 1gkIl2PlwS6wt0QmwCbAr1UwnjvVNioZBPRcHv/PLLf/0P2HQBHVESO7SMAhqaQoLf0V+LBOK/Qw
36569 WyH8EZE0vkHve52Xdf+XlcCWWC/qu0bXu+TZLg==
36570 -----END CERTIFICATE-----
36571
36572 AC Ra\xC3\xADz Certic\xC3\xA1mara S.A.
36573 ======================================
36574 -----BEGIN CERTIFICATE-----
36575 MIIGZjCCBE6gAwIBAgIPB35Sk3vgFeNX8GmMy+wMMA0GCSqGSIb3DQEBBQUAMHsxCzAJBgNVBAYT
36576 AkNPMUcwRQYDVQQKDD5Tb2NpZWRhZCBDYW1lcmFsIGRlIENlcnRpZmljYWNpw7NuIERpZ2l0YWwg
36577 LSBDZXJ0aWPDoW1hcmEgUy5BLjEjMCEGA1UEAwwaQUMgUmHDrXogQ2VydGljw6FtYXJhIFMuQS4w
36578 HhcNMDYxMTI3MjA0NjI5WhcNMzAwNDAyMjE0MjAyWjB7MQswCQYDVQQGEwJDTzFHMEUGA1UECgw+
36579 U29jaWVkYWQgQ2FtZXJhbCBkZSBDZXJ0aWZpY2FjacOzbiBEaWdpdGFsIC0gQ2VydGljw6FtYXJh
36580 IFMuQS4xIzAhBgNVBAMMGkFDIFJhw616IENlcnRpY8OhbWFyYSBTLkEuMIICIjANBgkqhkiG9w0B
36581 AQEFAAOCAg8AMIICCgKCAgEAq2uJo1PMSCMI+8PPUZYILrgIem08kBeGqentLhM0R7LQcNzJPNCN
36582 yu5LF6vQhbCnIwTLqKL85XXbQMpiiY9QngE9JlsYhBzLfDe3fezTf3MZsGqy2IiKLUV0qPezuMDU
36583 2s0iiXRNWhU5cxh0T7XrmafBHoi0wpOQY5fzp6cSsgkiBzPZkc0OnB8OIMfuuzONj8LSWKdf/WU3
36584 4ojC2I+GdV75LaeHM/J4Ny+LvB2GNzmxlPLYvEqcgxhaBvzz1NS6jBUJJfD5to0EfhcSM2tXSExP
36585 2yYe68yQ54v5aHxwD6Mq0Do43zeX4lvegGHTgNiRg0JaTASJaBE8rF9ogEHMYELODVoqDA+bMMCm
36586 8Ibbq0nXl21Ii/kDwFJnmxL3wvIumGVC2daa49AZMQyth9VXAnow6IYm+48jilSH5L887uvDdUhf
36587 HjlvgWJsxS3EF1QZtzeNnDeRyPYL1epjb4OsOMLzP96a++EjYfDIJss2yKHzMI+ko6Kh3VOz3vCa
36588 Mh+DkXkwwakfU5tTohVTP92dsxA7SH2JD/ztA/X7JWR1DhcZDY8AFmd5ekD8LVkH2ZD6mq093ICK
36589 5lw1omdMEWux+IBkAC1vImHFrEsm5VoQgpukg3s0956JkSCXjrdCx2bD0Omk1vUgjcTDlaxECp1b
36590 czwmPS9KvqfJpxAe+59QafMCAwEAAaOB5jCB4zAPBgNVHRMBAf8EBTADAQH/MA4GA1UdDwEB/wQE
36591 AwIBBjAdBgNVHQ4EFgQU0QnQ6dfOeXRU+Tows/RtLAMDG2gwgaAGA1UdIASBmDCBlTCBkgYEVR0g
36592 ADCBiTArBggrBgEFBQcCARYfaHR0cDovL3d3dy5jZXJ0aWNhbWFyYS5jb20vZHBjLzBaBggrBgEF
36593 BQcCAjBOGkxMaW1pdGFjaW9uZXMgZGUgZ2FyYW507WFzIGRlIGVzdGUgY2VydGlmaWNhZG8gc2Ug
36594 cHVlZGVuIGVuY29udHJhciBlbiBsYSBEUEMuMA0GCSqGSIb3DQEBBQUAA4ICAQBclLW4RZFNjmEf
36595 AygPU3zmpFmps4p6xbD/CHwso3EcIRNnoZUSQDWDg4902zNc8El2CoFS3UnUmjIz75uny3XlesuX
36596 EpBcunvFm9+7OSPI/5jOCk0iAUgHforA1SBClETvv3eiiWdIG0ADBaGJ7M9i4z0ldma/Jre7Ir5v
36597 /zlXdLp6yQGVwZVR6Kss+LGGIOk/yzVb0hfpKv6DExdA7ohiZVvVO2Dpezy4ydV/NgIlqmjCMRW3
36598 MGXrfx1IebHPOeJCgBbT9ZMj/EyXyVo3bHwi2ErN0o42gzmRkBDI8ck1fj+404HGIGQatlDCIaR4
36599 3NAvO2STdPCWkPHv+wlaNECW8DYSwaN0jJN+Qd53i+yG2dIPPy3RzECiiWZIHiCznCNZc6lEc7wk
36600 eZBWN7PGKX6jD/EpOe9+XCgycDWs2rjIdWb8m0w5R44bb5tNAlQiM+9hup4phO9OSzNHdpdqy35f
36601 /RWmnkJDW2ZaiogN9xa5P1FlK2Zqi9E4UqLWRhH6/JocdJ6PlwsCT2TG9WjTSy3/pDceiz+/RL5h
36602 RqGEPQgnTIEgd4kI6mdAXmwIUV80WoyWaM3X94nCHNMyAK9Sy9NgWyo6R35rMDOhYil/SrnhLecU
36603 Iw4OGEfhefwVVdCx/CVxY3UzHCMrr1zZ7Ud3YA47Dx7SwNxkBYn8eNZcLCZDqQ==
36604 -----END CERTIFICATE-----
36605
36606 TC TrustCenter Class 2 CA II
36607 ============================
36608 -----BEGIN CERTIFICATE-----
36609 MIIEqjCCA5KgAwIBAgIOLmoAAQACH9dSISwRXDswDQYJKoZIhvcNAQEFBQAwdjELMAkGA1UEBhMC
36610 REUxHDAaBgNVBAoTE1RDIFRydXN0Q2VudGVyIEdtYkgxIjAgBgNVBAsTGVRDIFRydXN0Q2VudGVy
36611 IENsYXNzIDIgQ0ExJTAjBgNVBAMTHFRDIFRydXN0Q2VudGVyIENsYXNzIDIgQ0EgSUkwHhcNMDYw
36612 MTEyMTQzODQzWhcNMjUxMjMxMjI1OTU5WjB2MQswCQYDVQQGEwJERTEcMBoGA1UEChMTVEMgVHJ1
36613 c3RDZW50ZXIgR21iSDEiMCAGA1UECxMZVEMgVHJ1c3RDZW50ZXIgQ2xhc3MgMiBDQTElMCMGA1UE
36614 AxMcVEMgVHJ1c3RDZW50ZXIgQ2xhc3MgMiBDQSBJSTCCASIwDQYJKoZIhvcNAQEBBQADggEPADCC
36615 AQoCggEBAKuAh5uO8MN8h9foJIIRszzdQ2Lu+MNF2ujhoF/RKrLqk2jftMjWQ+nEdVl//OEd+DFw
36616 IxuInie5e/060smp6RQvkL4DUsFJzfb95AhmC1eKokKguNV/aVyQMrKXDcpK3EY+AlWJU+MaWss2
36617 xgdW94zPEfRMuzBwBJWl9jmM/XOBCH2JXjIeIqkiRUuwZi4wzJ9l/fzLganx4Duvo4bRierERXlQ
36618 Xa7pIXSSTYtZgo+U4+lK8edJsBTj9WLL1XK9H7nSn6DNqPoByNkN39r8R52zyFTfSUrxIan+GE7u
36619 SNQZu+995OKdy1u2bv/jzVrndIIFuoAlOMvkaZ6vQaoahPUCAwEAAaOCATQwggEwMA8GA1UdEwEB
36620 /wQFMAMBAf8wDgYDVR0PAQH/BAQDAgEGMB0GA1UdDgQWBBTjq1RMgKHbVkO3kUrL84J6E1wIqzCB
36621 7QYDVR0fBIHlMIHiMIHfoIHcoIHZhjVodHRwOi8vd3d3LnRydXN0Y2VudGVyLmRlL2NybC92Mi90
36622 Y19jbGFzc18yX2NhX0lJLmNybIaBn2xkYXA6Ly93d3cudHJ1c3RjZW50ZXIuZGUvQ049VEMlMjBU
36623 cnVzdENlbnRlciUyMENsYXNzJTIwMiUyMENBJTIwSUksTz1UQyUyMFRydXN0Q2VudGVyJTIwR21i
36624 SCxPVT1yb290Y2VydHMsREM9dHJ1c3RjZW50ZXIsREM9ZGU/Y2VydGlmaWNhdGVSZXZvY2F0aW9u
36625 TGlzdD9iYXNlPzANBgkqhkiG9w0BAQUFAAOCAQEAjNfffu4bgBCzg/XbEeprS6iSGNn3Bzn1LL4G
36626 dXpoUxUc6krtXvwjshOg0wn/9vYua0Fxec3ibf2uWWuFHbhOIprtZjluS5TmVfwLG4t3wVMTZonZ
36627 KNaL80VKY7f9ewthXbhtvsPcW3nS7Yblok2+XnR8au0WOB9/WIFaGusyiC2y8zl3gK9etmF1Kdsj
36628 TYjKUCjLhdLTEKJZbtOTVAB6okaVhgWcqRmY5TFyDADiZ9lA4CQze28suVyrZZ0srHbqNZn1l7kP
36629 JOzHdiEoZa5X6AeIdUpWoNIFOqTmjZKILPPy4cHGYdtBxceb9w4aUUXCYWvcZCcXjFq32nQozZfk
36630 vQ==
36631 -----END CERTIFICATE-----
36632
36633 TC TrustCenter Class 3 CA II
36634 ============================
36635 -----BEGIN CERTIFICATE-----
36636 MIIEqjCCA5KgAwIBAgIOSkcAAQAC5aBd1j8AUb8wDQYJKoZIhvcNAQEFBQAwdjELMAkGA1UEBhMC
36637 REUxHDAaBgNVBAoTE1RDIFRydXN0Q2VudGVyIEdtYkgxIjAgBgNVBAsTGVRDIFRydXN0Q2VudGVy
36638 IENsYXNzIDMgQ0ExJTAjBgNVBAMTHFRDIFRydXN0Q2VudGVyIENsYXNzIDMgQ0EgSUkwHhcNMDYw
36639 MTEyMTQ0MTU3WhcNMjUxMjMxMjI1OTU5WjB2MQswCQYDVQQGEwJERTEcMBoGA1UEChMTVEMgVHJ1
36640 c3RDZW50ZXIgR21iSDEiMCAGA1UECxMZVEMgVHJ1c3RDZW50ZXIgQ2xhc3MgMyBDQTElMCMGA1UE
36641 AxMcVEMgVHJ1c3RDZW50ZXIgQ2xhc3MgMyBDQSBJSTCCASIwDQYJKoZIhvcNAQEBBQADggEPADCC
36642 AQoCggEBALTgu1G7OVyLBMVMeRwjhjEQY0NVJz/GRcekPewJDRoeIMJWHt4bNwcwIi9v8Qbxq63W
36643 yKthoy9DxLCyLfzDlml7forkzMA5EpBCYMnMNWju2l+QVl/NHE1bWEnrDgFPZPosPIlY2C8u4rBo
36644 6SI7dYnWRBpl8huXJh0obazovVkdKyT21oQDZogkAHhg8fir/gKya/si+zXmFtGt9i4S5Po1auUZ
36645 uV3bOx4a+9P/FRQI2AlqukWdFHlgfa9Aigdzs5OW03Q0jTo3Kd5c7PXuLjHCINy+8U9/I1LZW+Jk
36646 2ZyqBwi1Rb3R0DHBq1SfqdLDYmAD8bs5SpJKPQq5ncWg/jcCAwEAAaOCATQwggEwMA8GA1UdEwEB
36647 /wQFMAMBAf8wDgYDVR0PAQH/BAQDAgEGMB0GA1UdDgQWBBTUovyfs8PYA9NXXAek0CSnwPIA1DCB
36648 7QYDVR0fBIHlMIHiMIHfoIHcoIHZhjVodHRwOi8vd3d3LnRydXN0Y2VudGVyLmRlL2NybC92Mi90
36649 Y19jbGFzc18zX2NhX0lJLmNybIaBn2xkYXA6Ly93d3cudHJ1c3RjZW50ZXIuZGUvQ049VEMlMjBU
36650 cnVzdENlbnRlciUyMENsYXNzJTIwMyUyMENBJTIwSUksTz1UQyUyMFRydXN0Q2VudGVyJTIwR21i
36651 SCxPVT1yb290Y2VydHMsREM9dHJ1c3RjZW50ZXIsREM9ZGU/Y2VydGlmaWNhdGVSZXZvY2F0aW9u
36652 TGlzdD9iYXNlPzANBgkqhkiG9w0BAQUFAAOCAQEANmDkcPcGIEPZIxpC8vijsrlNirTzwppVMXzE
36653 O2eatN9NDoqTSheLG43KieHPOh6sHfGcMrSOWXaiQYUlN6AT0PV8TtXqluJucsG7Kv5sbviRmEb8
36654 yRtXW+rIGjs/sFGYPAfaLFkB2otE6OF0/ado3VS6g0bsyEa1+K+XwDsJHI/OcpY9M1ZwvJbL2NV9
36655 IJqDnxrcOfHFcqMRA/07QlIp2+gB95tejNaNhk4Z+rwcvsUhpYeeeC422wlxo3I0+GzjBgnyXlal
36656 092Y+tTmBvTwtiBjS+opvaqCZh77gaqnN60TGOaSw4HBM7uIHqHn4rS9MWwOUT1v+5ZWgOI2F9Hc
36657 5A==
36658 -----END CERTIFICATE-----
36659
36660 TC TrustCenter Universal CA I
36661 =============================
36662 -----BEGIN CERTIFICATE-----
36663 MIID3TCCAsWgAwIBAgIOHaIAAQAC7LdggHiNtgYwDQYJKoZIhvcNAQEFBQAweTELMAkGA1UEBhMC
36664 REUxHDAaBgNVBAoTE1RDIFRydXN0Q2VudGVyIEdtYkgxJDAiBgNVBAsTG1RDIFRydXN0Q2VudGVy
36665 IFVuaXZlcnNhbCBDQTEmMCQGA1UEAxMdVEMgVHJ1c3RDZW50ZXIgVW5pdmVyc2FsIENBIEkwHhcN
36666 MDYwMzIyMTU1NDI4WhcNMjUxMjMxMjI1OTU5WjB5MQswCQYDVQQGEwJERTEcMBoGA1UEChMTVEMg
36667 VHJ1c3RDZW50ZXIgR21iSDEkMCIGA1UECxMbVEMgVHJ1c3RDZW50ZXIgVW5pdmVyc2FsIENBMSYw
36668 JAYDVQQDEx1UQyBUcnVzdENlbnRlciBVbml2ZXJzYWwgQ0EgSTCCASIwDQYJKoZIhvcNAQEBBQAD
36669 ggEPADCCAQoCggEBAKR3I5ZEr5D0MacQ9CaHnPM42Q9e3s9B6DGtxnSRJJZ4Hgmgm5qVSkr1YnwC
36670 qMqs+1oEdjneX/H5s7/zA1hV0qq34wQi0fiU2iIIAI3TfCZdzHd55yx4Oagmcw6iXSVphU9VDprv
36671 xrlE4Vc93x9UIuVvZaozhDrzznq+VZeujRIPFDPiUHDDSYcTvFHe15gSWu86gzOSBnWLknwSaHtw
36672 ag+1m7Z3W0hZneTvWq3zwZ7U10VOylY0Ibw+F1tvdwxIAUMpsN0/lm7mlaoMwCC2/T42J5zjXM9O
36673 gdwZu5GQfezmlwQek8wiSdeXhrYTCjxDI3d+8NzmzSQfO4ObNDqDNOMCAwEAAaNjMGEwHwYDVR0j
36674 BBgwFoAUkqR1LKSevoFE63n8isWVpesQdXMwDwYDVR0TAQH/BAUwAwEB/zAOBgNVHQ8BAf8EBAMC
36675 AYYwHQYDVR0OBBYEFJKkdSyknr6BROt5/IrFlaXrEHVzMA0GCSqGSIb3DQEBBQUAA4IBAQAo0uCG
36676 1eb4e/CX3CJrO5UUVg8RMKWaTzqwOuAGy2X17caXJ/4l8lfmXpWMPmRgFVp/Lw0BxbFg/UU1z/Cy
36677 vwbZ71q+s2IhtNerNXxTPqYn8aEt2hojnczd7Dwtnic0XQ/CNnm8yUpiLe1r2X1BQ3y2qsrtYbE3
36678 ghUJGooWMNjsydZHcnhLEEYUjl8Or+zHL6sQ17bxbuyGssLoDZJz3KL0Dzq/YSMQiZxIQG5wALPT
36679 ujdEWBF6AmqI8Dc08BnprNRlc/ZpjGSUOnmFKbAWKwyCPwacx/0QK54PLLae4xW/2TYcuiUaUj0a
36680 7CIMHOCkoj3w6DnPgcB77V0fb8XQC9eY
36681 -----END CERTIFICATE-----
36682
36683 Deutsche Telekom Root CA 2
36684 ==========================
36685 -----BEGIN CERTIFICATE-----
36686 MIIDnzCCAoegAwIBAgIBJjANBgkqhkiG9w0BAQUFADBxMQswCQYDVQQGEwJERTEcMBoGA1UEChMT
36687 RGV1dHNjaGUgVGVsZWtvbSBBRzEfMB0GA1UECxMWVC1UZWxlU2VjIFRydXN0IENlbnRlcjEjMCEG
36688 A1UEAxMaRGV1dHNjaGUgVGVsZWtvbSBSb290IENBIDIwHhcNOTkwNzA5MTIxMTAwWhcNMTkwNzA5
36689 MjM1OTAwWjBxMQswCQYDVQQGEwJERTEcMBoGA1UEChMTRGV1dHNjaGUgVGVsZWtvbSBBRzEfMB0G
36690 A1UECxMWVC1UZWxlU2VjIFRydXN0IENlbnRlcjEjMCEGA1UEAxMaRGV1dHNjaGUgVGVsZWtvbSBS
36691 b290IENBIDIwggEiMA0GCSqGSIb3DQEBAQUAA4IBDwAwggEKAoIBAQCrC6M14IspFLEUha88EOQ5
36692 bzVdSq7d6mGNlUn0b2SjGmBmpKlAIoTZ1KXleJMOaAGtuU1cOs7TuKhCQN/Po7qCWWqSG6wcmtoI
36693 KyUn+WkjR/Hg6yx6m/UTAtB+NHzCnjwAWav12gz1MjwrrFDa1sPeg5TKqAyZMg4ISFZbavva4VhY
36694 AUlfckE8FQYBjl2tqriTtM2e66foai1SNNs671x1Udrb8zH57nGYMsRUFUQM+ZtV7a3fGAigo4aK
36695 Se5TBY8ZTNXeWHmb0mocQqvF1afPaA+W5OFhmHZhyJF81j4A4pFQh+GdCuatl9Idxjp9y7zaAzTV
36696 jlsB9WoHtxa2bkp/AgMBAAGjQjBAMB0GA1UdDgQWBBQxw3kbuvVT1xfgiXotF2wKsyudMzAPBgNV
36697 HRMECDAGAQH/AgEFMA4GA1UdDwEB/wQEAwIBBjANBgkqhkiG9w0BAQUFAAOCAQEAlGRZrTlk5ynr
36698 E/5aw4sTV8gEJPB0d8Bg42f76Ymmg7+Wgnxu1MM9756AbrsptJh6sTtU6zkXR34ajgv8HzFZMQSy
36699 zhfzLMdiNlXiItiJVbSYSKpk+tYcNthEeFpaIzpXl/V6ME+un2pMSyuOoAPjPuCp1NJ70rOo4nI8
36700 rZ7/gFnkm0W09juwzTkZmDLl6iFhkOQxIY40sfcvNUqFENrnijchvllj4PKFiDFT1FQUhXB59C4G
36701 dyd1Lx+4ivn+xbrYNuSD7Odlt79jWvNGr4GUN9RBjNYj1h7P9WgbRGOiWrqnNVmh5XAFmw4jV5mU
36702 Cm26OWMohpLzGITY+9HPBVZkVw==
36703 -----END CERTIFICATE-----
36704
36705 ComSign CA
36706 ==========
36707 -----BEGIN CERTIFICATE-----
36708 MIIDkzCCAnugAwIBAgIQFBOWgxRVjOp7Y+X8NId3RDANBgkqhkiG9w0BAQUFADA0MRMwEQYDVQQD
36709 EwpDb21TaWduIENBMRAwDgYDVQQKEwdDb21TaWduMQswCQYDVQQGEwJJTDAeFw0wNDAzMjQxMTMy
36710 MThaFw0yOTAzMTkxNTAyMThaMDQxEzARBgNVBAMTCkNvbVNpZ24gQ0ExEDAOBgNVBAoTB0NvbVNp
36711 Z24xCzAJBgNVBAYTAklMMIIBIjANBgkqhkiG9w0BAQEFAAOCAQ8AMIIBCgKCAQEA8ORUaSvTx49q
36712 ROR+WCf4C9DklBKK8Rs4OC8fMZwG1Cyn3gsqrhqg455qv588x26i+YtkbDqthVVRVKU4VbirgwTy
36713 P2Q298CNQ0NqZtH3FyrV7zb6MBBC11PN+fozc0yz6YQgitZBJzXkOPqUm7h65HkfM/sb2CEJKHxN
36714 GGleZIp6GZPKfuzzcuc3B1hZKKxC+cX/zT/npfo4sdAMx9lSGlPWgcxCejVb7Us6eva1jsz/D3zk
36715 YDaHL63woSV9/9JLEYhwVKZBqGdTUkJe5DSe5L6j7KpiXd3DTKaCQeQzC6zJMw9kglcq/QytNuEM
36716 rkvF7zuZ2SOzW120V+x0cAwqTwIDAQABo4GgMIGdMAwGA1UdEwQFMAMBAf8wPQYDVR0fBDYwNDAy
36717 oDCgLoYsaHR0cDovL2ZlZGlyLmNvbXNpZ24uY28uaWwvY3JsL0NvbVNpZ25DQS5jcmwwDgYDVR0P
36718 AQH/BAQDAgGGMB8GA1UdIwQYMBaAFEsBmz5WGmU2dst7l6qSBe4y5ygxMB0GA1UdDgQWBBRLAZs+
36719 VhplNnbLe5eqkgXuMucoMTANBgkqhkiG9w0BAQUFAAOCAQEA0Nmlfv4pYEWdfoPPbrxHbvUanlR2
36720 QnG0PFg/LUAlQvaBnPGJEMgOqnhPOAlXsDzACPw1jvFIUY0McXS6hMTXcpuEfDhOZAYnKuGntewI
36721 mbQKDdSFc8gS4TXt8QUxHXOZDOuWyt3T5oWq8Ir7dcHyCTxlZWTzTNity4hp8+SDtwy9F1qWF8pb
36722 /627HOkthIDYIb6FUtnUdLlphbpN7Sgy6/lhSuTENh4Z3G+EER+V9YMoGKgzkkMn3V0TBEVPh9VG
36723 zT2ouvDzuFYkRes3x+F2T3I5GN9+dHLHcy056mDmrRGiVod7w2ia/viMcKjfZTL0pECMocJEAw6U
36724 AGegcQCCSA==
36725 -----END CERTIFICATE-----
36726
36727 ComSign Secured CA
36728 ==================
36729 -----BEGIN CERTIFICATE-----
36730 MIIDqzCCApOgAwIBAgIRAMcoRwmzuGxFjB36JPU2TukwDQYJKoZIhvcNAQEFBQAwPDEbMBkGA1UE
36731 AxMSQ29tU2lnbiBTZWN1cmVkIENBMRAwDgYDVQQKEwdDb21TaWduMQswCQYDVQQGEwJJTDAeFw0w
36732 NDAzMjQxMTM3MjBaFw0yOTAzMTYxNTA0NTZaMDwxGzAZBgNVBAMTEkNvbVNpZ24gU2VjdXJlZCBD
36733 QTEQMA4GA1UEChMHQ29tU2lnbjELMAkGA1UEBhMCSUwwggEiMA0GCSqGSIb3DQEBAQUAA4IBDwAw
36734 ggEKAoIBAQDGtWhfHZQVw6QIVS3joFd67+l0Kru5fFdJGhFeTymHDEjWaueP1H5XJLkGieQcPOqs
36735 49ohgHMhCu95mGwfCP+hUH3ymBvJVG8+pSjsIQQPRbsHPaHA+iqYHU4Gk/v1iDurX8sWv+bznkqH
36736 7Rnqwp9D5PGBpX8QTz7RSmKtUxvLg/8HZaWSLWapW7ha9B20IZFKF3ueMv5WJDmyVIRD9YTC2LxB
36737 kMyd1mja6YJQqTtoz7VdApRgFrFD2UNd3V2Hbuq7s8lr9gOUCXDeFhF6K+h2j0kQmHe5Y1yLM5d1
36738 9guMsqtb3nQgJT/j8xH5h2iGNXHDHYwt6+UarA9z1YJZQIDTAgMBAAGjgacwgaQwDAYDVR0TBAUw
36739 AwEB/zBEBgNVHR8EPTA7MDmgN6A1hjNodHRwOi8vZmVkaXIuY29tc2lnbi5jby5pbC9jcmwvQ29t
36740 U2lnblNlY3VyZWRDQS5jcmwwDgYDVR0PAQH/BAQDAgGGMB8GA1UdIwQYMBaAFMFL7XC29z58ADsA
36741 j8c+DkWfHl3sMB0GA1UdDgQWBBTBS+1wtvc+fAA7AI/HPg5Fnx5d7DANBgkqhkiG9w0BAQUFAAOC
36742 AQEAFs/ukhNQq3sUnjO2QiBq1BW9Cav8cujvR3qQrFHBZE7piL1DRYHjZiM/EoZNGeQFsOY3wo3a
36743 BijJD4mkU6l1P7CW+6tMM1X5eCZGbxs2mPtCdsGCuY7e+0X5YxtiOzkGynd6qDwJz2w2PQ8KRUtp
36744 FhpFfTMDZflScZAmlaxMDPWLkz/MdXSFmLr/YnpNH4n+rr2UAJm/EaXc4HnFFgt9AmEd6oX5AhVP
36745 51qJThRv4zdLhfXBPGHg/QVBspJ/wx2g0K5SZGBrGMYmnNj1ZOQ2GmKfig8+/21OGVZOIJFsnzQz
36746 OjRXUDpvgV4GxvU+fE6OK85lBi5d0ipTdF7Tbieejw==
36747 -----END CERTIFICATE-----
36748
36749 Cybertrust Global Root
36750 ======================
36751 -----BEGIN CERTIFICATE-----
36752 MIIDoTCCAomgAwIBAgILBAAAAAABD4WqLUgwDQYJKoZIhvcNAQEFBQAwOzEYMBYGA1UEChMPQ3li
36753 ZXJ0cnVzdCwgSW5jMR8wHQYDVQQDExZDeWJlcnRydXN0IEdsb2JhbCBSb290MB4XDTA2MTIxNTA4
36754 MDAwMFoXDTIxMTIxNTA4MDAwMFowOzEYMBYGA1UEChMPQ3liZXJ0cnVzdCwgSW5jMR8wHQYDVQQD
36755 ExZDeWJlcnRydXN0IEdsb2JhbCBSb290MIIBIjANBgkqhkiG9w0BAQEFAAOCAQ8AMIIBCgKCAQEA
36756 +Mi8vRRQZhP/8NN57CPytxrHjoXxEnOmGaoQ25yiZXRadz5RfVb23CO21O1fWLE3TdVJDm71aofW
36757 0ozSJ8bi/zafmGWgE07GKmSb1ZASzxQG9Dvj1Ci+6A74q05IlG2OlTEQXO2iLb3VOm2yHLtgwEZL
36758 AfVJrn5GitB0jaEMAs7u/OePuGtm839EAL9mJRQr3RAwHQeWP032a7iPt3sMpTjr3kfb1V05/Iin
36759 89cqdPHoWqI7n1C6poxFNcJQZZXcY4Lv3b93TZxiyWNzFtApD0mpSPCzqrdsxacwOUBdrsTiXSZT
36760 8M4cIwhhqJQZugRiQOwfOHB3EgZxpzAYXSUnpQIDAQABo4GlMIGiMA4GA1UdDwEB/wQEAwIBBjAP
36761 BgNVHRMBAf8EBTADAQH/MB0GA1UdDgQWBBS2CHsNesysIEyGVjJez6tuhS1wVzA/BgNVHR8EODA2
36762 MDSgMqAwhi5odHRwOi8vd3d3Mi5wdWJsaWMtdHJ1c3QuY29tL2NybC9jdC9jdHJvb3QuY3JsMB8G
36763 A1UdIwQYMBaAFLYIew16zKwgTIZWMl7Pq26FLXBXMA0GCSqGSIb3DQEBBQUAA4IBAQBW7wojoFRO
36764 lZfJ+InaRcHUowAl9B8Tq7ejhVhpwjCt2BWKLePJzYFa+HMjWqd8BfP9IjsO0QbE2zZMcwSO5bAi
36765 5MXzLqXZI+O4Tkogp24CJJ8iYGd7ix1yCcUxXOl5n4BHPa2hCwcUPUf/A2kaDAtE52Mlp3+yybh2
36766 hO0j9n0Hq0V+09+zv+mKts2oomcrUtW3ZfA5TGOgkXmTUg9U3YO7n9GPp1Nzw8v/MOx8BLjYRB+T
36767 X3EJIrduPuocA06dGiBh+4E37F78CkWr1+cXVdCg6mCbpvbjjFspwgZgFJ0tl0ypkxWdYcQBX0jW
36768 WL1WMRJOEcgh4LMRkWXbtKaIOM5V
36769 -----END CERTIFICATE-----
36770
36771 ePKI Root Certification Authority
36772 =================================
36773 -----BEGIN CERTIFICATE-----
36774 MIIFsDCCA5igAwIBAgIQFci9ZUdcr7iXAF7kBtK8nTANBgkqhkiG9w0BAQUFADBeMQswCQYDVQQG
36775 EwJUVzEjMCEGA1UECgwaQ2h1bmdod2EgVGVsZWNvbSBDby4sIEx0ZC4xKjAoBgNVBAsMIWVQS0kg
36776 Um9vdCBDZXJ0aWZpY2F0aW9uIEF1dGhvcml0eTAeFw0wNDEyMjAwMjMxMjdaFw0zNDEyMjAwMjMx
36777 MjdaMF4xCzAJBgNVBAYTAlRXMSMwIQYDVQQKDBpDaHVuZ2h3YSBUZWxlY29tIENvLiwgTHRkLjEq
36778 MCgGA1UECwwhZVBLSSBSb290IENlcnRpZmljYXRpb24gQXV0aG9yaXR5MIICIjANBgkqhkiG9w0B
36779 AQEFAAOCAg8AMIICCgKCAgEA4SUP7o3biDN1Z82tH306Tm2d0y8U82N0ywEhajfqhFAHSyZbCUNs
36780 IZ5qyNUD9WBpj8zwIuQf5/dqIjG3LBXy4P4AakP/h2XGtRrBp0xtInAhijHyl3SJCRImHJ7K2RKi
36781 lTza6We/CKBk49ZCt0Xvl/T29de1ShUCWH2YWEtgvM3XDZoTM1PRYfl61dd4s5oz9wCGzh1NlDiv
36782 qOx4UXCKXBCDUSH3ET00hl7lSM2XgYI1TBnsZfZrxQWh7kcT1rMhJ5QQCtkkO7q+RBNGMD+XPNjX
36783 12ruOzjjK9SXDrkb5wdJfzcq+Xd4z1TtW0ado4AOkUPB1ltfFLqfpo0kR0BZv3I4sjZsN/+Z0V0O
36784 WQqraffAsgRFelQArr5T9rXn4fg8ozHSqf4hUmTFpmfwdQcGlBSBVcYn5AGPF8Fqcde+S/uUWH1+
36785 ETOxQvdibBjWzwloPn9s9h6PYq2lY9sJpx8iQkEeb5mKPtf5P0B6ebClAZLSnT0IFaUQAS2zMnao
36786 lQ2zepr7BxB4EW/hj8e6DyUadCrlHJhBmd8hh+iVBmoKs2pHdmX2Os+PYhcZewoozRrSgx4hxyy/
36787 vv9haLdnG7t4TY3OZ+XkwY63I2binZB1NJipNiuKmpS5nezMirH4JYlcWrYvjB9teSSnUmjDhDXi
36788 Zo1jDiVN1Rmy5nk3pyKdVDECAwEAAaNqMGgwHQYDVR0OBBYEFB4M97Zn8uGSJglFwFU5Lnc/Qkqi
36789 MAwGA1UdEwQFMAMBAf8wOQYEZyoHAAQxMC8wLQIBADAJBgUrDgMCGgUAMAcGBWcqAwAABBRFsMLH
36790 ClZ87lt4DJX5GFPBphzYEDANBgkqhkiG9w0BAQUFAAOCAgEACbODU1kBPpVJufGBuvl2ICO1J2B0
36791 1GqZNF5sAFPZn/KmsSQHRGoqxqWOeBLoR9lYGxMqXnmbnwoqZ6YlPwZpVnPDimZI+ymBV3QGypzq
36792 KOg4ZyYr8dW1P2WT+DZdjo2NQCCHGervJ8A9tDkPJXtoUHRVnAxZfVo9QZQlUgjgRywVMRnVvwdV
36793 xrsStZf0X4OFunHB2WyBEXYKCrC/gpf36j36+uwtqSiUO1bd0lEursC9CBWMd1I0ltabrNMdjmEP
36794 NXubrjlpC2JgQCA2j6/7Nu4tCEoduL+bXPjqpRugc6bY+G7gMwRfaKonh+3ZwZCc7b3jajWvY9+r
36795 GNm65ulK6lCKD2GTHuItGeIwlDWSXQ62B68ZgI9HkFFLLk3dheLSClIKF5r8GrBQAuUBo2M3IUxE
36796 xJtRmREOc5wGj1QupyheRDmHVi03vYVElOEMSyycw5KFNGHLD7ibSkNS/jQ6fbjpKdx2qcgw+BRx
36797 gMYeNkh0IkFch4LoGHGLQYlE535YW6i4jRPpp2zDR+2zGp1iro2C6pSe3VkQw63d4k3jMdXH7Ojy
36798 sP6SHhYKGvzZ8/gntsm+HbRsZJB/9OTEW9c3rkIO3aQab3yIVMUWbuF6aC74Or8NpDyJO3inTmOD
36799 BCEIZ43ygknQW/2xzQ+DhNQ+IIX3Sj0rnP0qCglN6oH4EZw=
36800 -----END CERTIFICATE-----
36801
36802 T\xc3\x9c\x42\xC4\xB0TAK UEKAE K\xC3\xB6k Sertifika Hizmet Sa\xC4\x9Flay\xc4\xb1\x63\xc4\xb1s\xc4\xb1 - S\xC3\xBCr\xC3\xBCm 3
36803 =============================================================================================================================
36804 -----BEGIN CERTIFICATE-----
36805 MIIFFzCCA/+gAwIBAgIBETANBgkqhkiG9w0BAQUFADCCASsxCzAJBgNVBAYTAlRSMRgwFgYDVQQH
36806 DA9HZWJ6ZSAtIEtvY2FlbGkxRzBFBgNVBAoMPlTDvHJraXllIEJpbGltc2VsIHZlIFRla25vbG9q
36807 aWsgQXJhxZ90xLFybWEgS3VydW11IC0gVMOcQsSwVEFLMUgwRgYDVQQLDD9VbHVzYWwgRWxla3Ry
36808 b25payB2ZSBLcmlwdG9sb2ppIEFyYcWfdMSxcm1hIEVuc3RpdMO8c8O8IC0gVUVLQUUxIzAhBgNV
36809 BAsMGkthbXUgU2VydGlmaWthc3lvbiBNZXJrZXppMUowSAYDVQQDDEFUw5xCxLBUQUsgVUVLQUUg
36810 S8O2ayBTZXJ0aWZpa2EgSGl6bWV0IFNhxJ9sYXnEsWPEsXPEsSAtIFPDvHLDvG0gMzAeFw0wNzA4
36811 MjQxMTM3MDdaFw0xNzA4MjExMTM3MDdaMIIBKzELMAkGA1UEBhMCVFIxGDAWBgNVBAcMD0dlYnpl
36812 IC0gS29jYWVsaTFHMEUGA1UECgw+VMO8cmtpeWUgQmlsaW1zZWwgdmUgVGVrbm9sb2ppayBBcmHF
36813 n3TEsXJtYSBLdXJ1bXUgLSBUw5xCxLBUQUsxSDBGBgNVBAsMP1VsdXNhbCBFbGVrdHJvbmlrIHZl
36814 IEtyaXB0b2xvamkgQXJhxZ90xLFybWEgRW5zdGl0w7xzw7wgLSBVRUtBRTEjMCEGA1UECwwaS2Ft
36815 dSBTZXJ0aWZpa2FzeW9uIE1lcmtlemkxSjBIBgNVBAMMQVTDnELEsFRBSyBVRUtBRSBLw7ZrIFNl
36816 cnRpZmlrYSBIaXptZXQgU2HEn2xhecSxY8Sxc8SxIC0gU8O8csO8bSAzMIIBIjANBgkqhkiG9w0B
36817 AQEFAAOCAQ8AMIIBCgKCAQEAim1L/xCIOsP2fpTo6iBkcK4hgb46ezzb8R1Sf1n68yJMlaCQvEhO
36818 Eav7t7WNeoMojCZG2E6VQIdhn8WebYGHV2yKO7Rm6sxA/OOqbLLLAdsyv9Lrhc+hDVXDWzhXcLh1
36819 xnnRFDDtG1hba+818qEhTsXOfJlfbLm4IpNQp81McGq+agV/E5wrHur+R84EpW+sky58K5+eeROR
36820 6Oqeyjh1jmKwlZMq5d/pXpduIF9fhHpEORlAHLpVK/swsoHvhOPc7Jg4OQOFCKlUAwUp8MmPi+oL
36821 hmUZEdPpCSPeaJMDyTYcIW7OjGbxmTDY17PDHfiBLqi9ggtm/oLL4eAagsNAgQIDAQABo0IwQDAd
36822 BgNVHQ4EFgQUvYiHyY/2pAoLquvF/pEjnatKijIwDgYDVR0PAQH/BAQDAgEGMA8GA1UdEwEB/wQF
36823 MAMBAf8wDQYJKoZIhvcNAQEFBQADggEBAB18+kmPNOm3JpIWmgV050vQbTlswyb2zrgxvMTfvCr4
36824 N5EY3ATIZJkrGG2AA1nJrvhY0D7twyOfaTyGOBye79oneNGEN3GKPEs5z35FBtYt2IpNeBLWrcLT
36825 y9LQQfMmNkqblWwM7uXRQydmwYj3erMgbOqwaSvHIOgMA8RBBZniP+Rr+KCGgceExh/VS4ESshYh
36826 LBOhgLJeDEoTniDYYkCrkOpkSi+sDQESeUWoL4cZaMjihccwsnX5OD+ywJO0a+IDRM5noN+J1q2M
36827 dqMTw5RhK2vZbMEHCiIHhWyFJEapvj+LeISCfiQMnf2BN+MlqO02TpUsyZyQ2uypQjyttgI=
36828 -----END CERTIFICATE-----
36829
36830 Buypass Class 2 CA 1
36831 ====================
36832 -----BEGIN CERTIFICATE-----
36833 MIIDUzCCAjugAwIBAgIBATANBgkqhkiG9w0BAQUFADBLMQswCQYDVQQGEwJOTzEdMBsGA1UECgwU
36834 QnV5cGFzcyBBUy05ODMxNjMzMjcxHTAbBgNVBAMMFEJ1eXBhc3MgQ2xhc3MgMiBDQSAxMB4XDTA2
36835 MTAxMzEwMjUwOVoXDTE2MTAxMzEwMjUwOVowSzELMAkGA1UEBhMCTk8xHTAbBgNVBAoMFEJ1eXBh
36836 c3MgQVMtOTgzMTYzMzI3MR0wGwYDVQQDDBRCdXlwYXNzIENsYXNzIDIgQ0EgMTCCASIwDQYJKoZI
36837 hvcNAQEBBQADggEPADCCAQoCggEBAIs8B0XY9t/mx8q6jUPFR42wWsE425KEHK8T1A9vNkYgxC7M
36838 cXA0ojTTNy7Y3Tp3L8DrKehc0rWpkTSHIln+zNvnma+WwajHQN2lFYxuyHyXA8vmIPLXl18xoS83
36839 0r7uvqmtqEyeIWZDO6i88wmjONVZJMHCR3axiFyCO7srpgTXjAePzdVBHfCuuCkslFJgNJQ72uA4
36840 0Z0zPhX0kzLFANq1KWYOOngPIVJfAuWSeyXTkh4vFZ2B5J2O6O+JzhRMVB0cgRJNcKi+EAUXfh/R
36841 uFdV7c27UsKwHnjCTTZoy1YmwVLBvXb3WNVyfh9EdrsAiR0WnVE1703CVu9r4Iw7DekCAwEAAaNC
36842 MEAwDwYDVR0TAQH/BAUwAwEB/zAdBgNVHQ4EFgQUP42aWYv8e3uco684sDntkHGA1sgwDgYDVR0P
36843 AQH/BAQDAgEGMA0GCSqGSIb3DQEBBQUAA4IBAQAVGn4TirnoB6NLJzKyQJHyIdFkhb5jatLPgcIV
36844 1Xp+DCmsNx4cfHZSldq1fyOhKXdlyTKdqC5Wq2B2zha0jX94wNWZUYN/Xtm+DKhQ7SLHrQVMdvvt
36845 7h5HZPb3J31cKA9FxVxiXqaakZG3Uxcu3K1gnZZkOb1naLKuBctN518fV4bVIJwo+28TOPX2EZL2
36846 fZleHwzoq0QkKXJAPTZSr4xYkHPB7GEseaHsh7U/2k3ZIQAw3pDaDtMaSKk+hQsUi4y8QZ5q9w5w
36847 wDX3OaJdZtB7WZ+oRxKaJyOkLY4ng5IgodcVf/EuGO70SH8vf/GhGLWhC5SgYiAynB321O+/TIho
36848 -----END CERTIFICATE-----
36849
36850 Buypass Class 3 CA 1
36851 ====================
36852 -----BEGIN CERTIFICATE-----
36853 MIIDUzCCAjugAwIBAgIBAjANBgkqhkiG9w0BAQUFADBLMQswCQYDVQQGEwJOTzEdMBsGA1UECgwU
36854 QnV5cGFzcyBBUy05ODMxNjMzMjcxHTAbBgNVBAMMFEJ1eXBhc3MgQ2xhc3MgMyBDQSAxMB4XDTA1
36855 MDUwOTE0MTMwM1oXDTE1MDUwOTE0MTMwM1owSzELMAkGA1UEBhMCTk8xHTAbBgNVBAoMFEJ1eXBh
36856 c3MgQVMtOTgzMTYzMzI3MR0wGwYDVQQDDBRCdXlwYXNzIENsYXNzIDMgQ0EgMTCCASIwDQYJKoZI
36857 hvcNAQEBBQADggEPADCCAQoCggEBAKSO13TZKWTeXx+HgJHqTjnmGcZEC4DVC69TB4sSveZn8AKx
36858 ifZgisRbsELRwCGoy+Gb72RRtqfPFfV0gGgEkKBYouZ0plNTVUhjP5JW3SROjvi6K//zNIqeKNc0
36859 n6wv1g/xpC+9UrJJhW05NfBEMJNGJPO251P7vGGvqaMU+8IXF4Rs4HyI+MkcVyzwPX6UvCWThOia
36860 AJpFBUJXgPROztmuOfbIUxAMZTpHe2DC1vqRycZxbL2RhzyRhkmr8w+gbCZ2Xhysm3HljbybIR6c
36861 1jh+JIAVMYKWsUnTYjdbiAwKYjT+p0h+mbEwi5A3lRyoH6UsjfRVyNvdWQrCrXig9IsCAwEAAaNC
36862 MEAwDwYDVR0TAQH/BAUwAwEB/zAdBgNVHQ4EFgQUOBTmyPCppAP0Tj4io1vy1uCtQHQwDgYDVR0P
36863 AQH/BAQDAgEGMA0GCSqGSIb3DQEBBQUAA4IBAQABZ6OMySU9E2NdFm/soT4JXJEVKirZgCFPBdy7
36864 pYmrEzMqnji3jG8CcmPHc3ceCQa6Oyh7pEfJYWsICCD8igWKH7y6xsL+z27sEzNxZy5p+qksP2bA
36865 EllNC1QCkoS72xLvg3BweMhT+t/Gxv/ciC8HwEmdMldg0/L2mSlf56oBzKwzqBwKu5HEA6BvtjT5
36866 htOzdlSY9EqBs1OdTUDs5XcTRa9bqh/YL0yCe/4qxFi7T/ye/QNlGioOw6UgFpRreaaiErS7GqQj
36867 el/wroQk5PMr+4okoyeYZdowdXb8GZHo2+ubPzK/QJcHJrrM85SFSnonk8+QQtS4Wxam58tAA915
36868 -----END CERTIFICATE-----
36869
36870 EBG Elektronik Sertifika Hizmet Sa\xC4\x9Flay\xc4\xb1\x63\xc4\xb1s\xc4\xb1
36871 ==========================================================================
36872 -----BEGIN CERTIFICATE-----
36873 MIIF5zCCA8+gAwIBAgIITK9zQhyOdAIwDQYJKoZIhvcNAQEFBQAwgYAxODA2BgNVBAMML0VCRyBF
36874 bGVrdHJvbmlrIFNlcnRpZmlrYSBIaXptZXQgU2HEn2xhecSxY8Sxc8SxMTcwNQYDVQQKDC5FQkcg
36875 QmlsacWfaW0gVGVrbm9sb2ppbGVyaSB2ZSBIaXptZXRsZXJpIEEuxZ4uMQswCQYDVQQGEwJUUjAe
36876 Fw0wNjA4MTcwMDIxMDlaFw0xNjA4MTQwMDMxMDlaMIGAMTgwNgYDVQQDDC9FQkcgRWxla3Ryb25p
36877 ayBTZXJ0aWZpa2EgSGl6bWV0IFNhxJ9sYXnEsWPEsXPEsTE3MDUGA1UECgwuRUJHIEJpbGnFn2lt
36878 IFRla25vbG9qaWxlcmkgdmUgSGl6bWV0bGVyaSBBLsWeLjELMAkGA1UEBhMCVFIwggIiMA0GCSqG
36879 SIb3DQEBAQUAA4ICDwAwggIKAoICAQDuoIRh0DpqZhAy2DE4f6en5f2h4fuXd7hxlugTlkaDT7by
36880 X3JWbhNgpQGR4lvFzVcfd2NR/y8927k/qqk153nQ9dAktiHq6yOU/im/+4mRDGSaBUorzAzu8T2b
36881 gmmkTPiab+ci2hC6X5L8GCcKqKpE+i4stPtGmggDg3KriORqcsnlZR9uKg+ds+g75AxuetpX/dfr
36882 eYteIAbTdgtsApWjluTLdlHRKJ2hGvxEok3MenaoDT2/F08iiFD9rrbskFBKW5+VQarKD7JK/oCZ
36883 TqNGFav4c0JqwmZ2sQomFd2TkuzbqV9UIlKRcF0T6kjsbgNs2d1s/OsNA/+mgxKb8amTD8UmTDGy
36884 Y5lhcucqZJnSuOl14nypqZoaqsNW2xCaPINStnuWt6yHd6i58mcLlEOzrz5z+kI2sSXFCjEmN1Zn
36885 uqMLfdb3ic1nobc6HmZP9qBVFCVMLDMNpkGMvQQxahByCp0OLna9XvNRiYuoP1Vzv9s6xiQFlpJI
36886 qkuNKgPlV5EQ9GooFW5Hd4RcUXSfGenmHmMWOeMRFeNYGkS9y8RsZteEBt8w9DeiQyJ50hBs37vm
36887 ExH8nYQKE3vwO9D8owrXieqWfo1IhR5kX9tUoqzVegJ5a9KK8GfaZXINFHDk6Y54jzJ0fFfy1tb0
36888 Nokb+Clsi7n2l9GkLqq+CxnCRelwXQIDAJ3Zo2MwYTAPBgNVHRMBAf8EBTADAQH/MA4GA1UdDwEB
36889 /wQEAwIBBjAdBgNVHQ4EFgQU587GT/wWZ5b6SqMHwQSny2re2kcwHwYDVR0jBBgwFoAU587GT/wW
36890 Z5b6SqMHwQSny2re2kcwDQYJKoZIhvcNAQEFBQADggIBAJuYml2+8ygjdsZs93/mQJ7ANtyVDR2t
36891 FcU22NU57/IeIl6zgrRdu0waypIN30ckHrMk2pGI6YNw3ZPX6bqz3xZaPt7gyPvT/Wwp+BVGoGgm
36892 zJNSroIBk5DKd8pNSe/iWtkqvTDOTLKBtjDOWU/aWR1qeqRFsIImgYZ29fUQALjuswnoT4cCB64k
36893 XPBfrAowzIpAoHMEwfuJJPaaHFy3PApnNgUIMbOv2AFoKuB4j3TeuFGkjGwgPaL7s9QJ/XvCgKqT
36894 bCmYIai7FvOpEl90tYeY8pUm3zTvilORiF0alKM/fCL414i6poyWqD1SNGKfAB5UVUJnxk1Gj7sU
36895 RT0KlhaOEKGXmdXTMIXM3rRyt7yKPBgpaP3ccQfuJDlq+u2lrDgv+R4QDgZxGhBM/nV+/x5XOULK
36896 1+EVoVZVWRvRo68R2E7DpSvvkL/A7IITW43WciyTTo9qKd+FPNMN4KIYEsxVL0e3p5sC/kH2iExt
36897 2qkBR4NkJ2IQgtYSe14DHzSpyZH+r11thie3I6p1GMog57AP14kOpmciY/SDQSsGS7tY1dHXt7kQ
36898 Y9iJSrSq3RZj9W6+YKH47ejWkE8axsWgKdOnIaj1Wjz3x0miIZpKlVIglnKaZsv30oZDfCK+lvm9
36899 AahH3eU7QPl1K5srRmSGjR70j/sHd9DqSaIcjVIUpgqT
36900 -----END CERTIFICATE-----
36901
36902 certSIGN ROOT CA
36903 ================
36904 -----BEGIN CERTIFICATE-----
36905 MIIDODCCAiCgAwIBAgIGIAYFFnACMA0GCSqGSIb3DQEBBQUAMDsxCzAJBgNVBAYTAlJPMREwDwYD
36906 VQQKEwhjZXJ0U0lHTjEZMBcGA1UECxMQY2VydFNJR04gUk9PVCBDQTAeFw0wNjA3MDQxNzIwMDRa
36907 Fw0zMTA3MDQxNzIwMDRaMDsxCzAJBgNVBAYTAlJPMREwDwYDVQQKEwhjZXJ0U0lHTjEZMBcGA1UE
36908 CxMQY2VydFNJR04gUk9PVCBDQTCCASIwDQYJKoZIhvcNAQEBBQADggEPADCCAQoCggEBALczuX7I
36909 JUqOtdu0KBuqV5Do0SLTZLrTk+jUrIZhQGpgV2hUhE28alQCBf/fm5oqrl0Hj0rDKH/v+yv6efHH
36910 rfAQUySQi2bJqIirr1qjAOm+ukbuW3N7LBeCgV5iLKECZbO9xSsAfsT8AzNXDe3i+s5dRdY4zTW2
36911 ssHQnIFKquSyAVwdj1+ZxLGt24gh65AIgoDzMKND5pCCrlUoSe1b16kQOA7+j0xbm0bqQfWwCHTD
36912 0IgztnzXdN/chNFDDnU5oSVAKOp4yw4sLjmdjItuFhwvJoIQ4uNllAoEwF73XVv4EOLQunpL+943
36913 AAAaWyjj0pxzPjKHmKHJUS/X3qwzs08CAwEAAaNCMEAwDwYDVR0TAQH/BAUwAwEB/zAOBgNVHQ8B
36914 Af8EBAMCAcYwHQYDVR0OBBYEFOCMm9slSbPxfIbWskKHC9BroNnkMA0GCSqGSIb3DQEBBQUAA4IB
36915 AQA+0hyJLjX8+HXd5n9liPRyTMks1zJO890ZeUe9jjtbkw9QSSQTaxQGcu8J06Gh40CEyecYMnQ8
36916 SG4Pn0vU9x7Tk4ZkVJdjclDVVc/6IJMCopvDI5NOFlV2oHB5bc0hH88vLbwZ44gx+FkagQnIl6Z0
36917 x2DEW8xXjrJ1/RsCCdtZb3KTafcxQdaIOL+Hsr0Wefmq5L6IJd1hJyMctTEHBDa0GpC9oHRxUIlt
36918 vBTjD4au8as+x6AJzKNI0eDbZOeStc+vckNwi/nDhDwTqn6Sm1dTk/pwwpEOMfmbZ13pljheX7Nz
36919 TogVZ96edhBiIL5VaZVDADlN9u6wWk5JRFRYX0KD
36920 -----END CERTIFICATE-----
36921
36922 CNNIC ROOT
36923 ==========
36924 -----BEGIN CERTIFICATE-----
36925 MIIDVTCCAj2gAwIBAgIESTMAATANBgkqhkiG9w0BAQUFADAyMQswCQYDVQQGEwJDTjEOMAwGA1UE
36926 ChMFQ05OSUMxEzARBgNVBAMTCkNOTklDIFJPT1QwHhcNMDcwNDE2MDcwOTE0WhcNMjcwNDE2MDcw
36927 OTE0WjAyMQswCQYDVQQGEwJDTjEOMAwGA1UEChMFQ05OSUMxEzARBgNVBAMTCkNOTklDIFJPT1Qw
36928 ggEiMA0GCSqGSIb3DQEBAQUAA4IBDwAwggEKAoIBAQDTNfc/c3et6FtzF8LRb+1VvG7q6KR5smzD
36929 o+/hn7E7SIX1mlwhIhAsxYLO2uOabjfhhyzcuQxauohV3/2q2x8x6gHx3zkBwRP9SFIhxFXf2tiz
36930 VHa6dLG3fdfA6PZZxU3Iva0fFNrfWEQlMhkqx35+jq44sDB7R3IJMfAw28Mbdim7aXZOV/kbZKKT
36931 VrdvmW7bCgScEeOAH8tjlBAKqeFkgjH5jCftppkA9nCTGPihNIaj3XrCGHn2emU1z5DrvTOTn1Or
36932 czvmmzQgLx3vqR1jGqCA2wMv+SYahtKNu6m+UjqHZ0gNv7Sg2Ca+I19zN38m5pIEo3/PIKe38zrK
36933 y5nLAgMBAAGjczBxMBEGCWCGSAGG+EIBAQQEAwIABzAfBgNVHSMEGDAWgBRl8jGtKvf33VKWCscC
36934 wQ7vptU7ETAPBgNVHRMBAf8EBTADAQH/MAsGA1UdDwQEAwIB/jAdBgNVHQ4EFgQUZfIxrSr3991S
36935 lgrHAsEO76bVOxEwDQYJKoZIhvcNAQEFBQADggEBAEs17szkrr/Dbq2flTtLP1se31cpolnKOOK5
36936 Gv+e5m4y3R6u6jW39ZORTtpC4cMXYFDy0VwmuYK36m3knITnA3kXr5g9lNvHugDnuL8BV8F3RTIM
36937 O/G0HAiw/VGgod2aHRM2mm23xzy54cXZF/qD1T0VoDy7HgviyJA/qIYM/PmLXoXLT1tLYhFHxUV8
36938 BS9BsZ4QaRuZluBVeftOhpm4lNqGOGqTo+fLbuXf6iFViZx9fX+Y9QCJ7uOEwFyWtcVG6kbghVW2
36939 G8kS1sHNzYDzAgE8yGnLRUhj2JTQ7IUOO04RZfSCjKY9ri4ilAnIXOo8gV0WKgOXFlUJ24pBgp5m
36940 mxE=
36941 -----END CERTIFICATE-----
36942
36943 ApplicationCA - Japanese Government
36944 ===================================
36945 -----BEGIN CERTIFICATE-----
36946 MIIDoDCCAoigAwIBAgIBMTANBgkqhkiG9w0BAQUFADBDMQswCQYDVQQGEwJKUDEcMBoGA1UEChMT
36947 SmFwYW5lc2UgR292ZXJubWVudDEWMBQGA1UECxMNQXBwbGljYXRpb25DQTAeFw0wNzEyMTIxNTAw
36948 MDBaFw0xNzEyMTIxNTAwMDBaMEMxCzAJBgNVBAYTAkpQMRwwGgYDVQQKExNKYXBhbmVzZSBHb3Zl
36949 cm5tZW50MRYwFAYDVQQLEw1BcHBsaWNhdGlvbkNBMIIBIjANBgkqhkiG9w0BAQEFAAOCAQ8AMIIB
36950 CgKCAQEAp23gdE6Hj6UG3mii24aZS2QNcfAKBZuOquHMLtJqO8F6tJdhjYq+xpqcBrSGUeQ3DnR4
36951 fl+Kf5Sk10cI/VBaVuRorChzoHvpfxiSQE8tnfWuREhzNgaeZCw7NCPbXCbkcXmP1G55IrmTwcrN
36952 wVbtiGrXoDkhBFcsovW8R0FPXjQilbUfKW1eSvNNcr5BViCH/OlQR9cwFO5cjFW6WY2H/CPek9AE
36953 jP3vbb3QesmlOmpyM8ZKDQUXKi17safY1vC+9D/qDihtQWEjdnjDuGWk81quzMKq2edY3rZ+nYVu
36954 nyoKb58DKTCXKB28t89UKU5RMfkntigm/qJj5kEW8DOYRwIDAQABo4GeMIGbMB0GA1UdDgQWBBRU
36955 WssmP3HMlEYNllPqa0jQk/5CdTAOBgNVHQ8BAf8EBAMCAQYwWQYDVR0RBFIwUKROMEwxCzAJBgNV
36956 BAYTAkpQMRgwFgYDVQQKDA/ml6XmnKzlm73mlL/lupwxIzAhBgNVBAsMGuOCouODl+ODquOCseOD
36957 vOOCt+ODp+ODs0NBMA8GA1UdEwEB/wQFMAMBAf8wDQYJKoZIhvcNAQEFBQADggEBADlqRHZ3ODrs
36958 o2dGD/mLBqj7apAxzn7s2tGJfHrrLgy9mTLnsCTWw//1sogJhyzjVOGjprIIC8CFqMjSnHH2HZ9g
36959 /DgzE+Ge3Atf2hZQKXsvcJEPmbo0NI2VdMV+eKlmXb3KIXdCEKxmJj3ekav9FfBv7WxfEPjzFvYD
36960 io+nEhEMy/0/ecGc/WLuo89UDNErXxc+4z6/wCs+CZv+iKZ+tJIX/COUgb1up8WMwusRRdv4QcmW
36961 dupwX3kSa+SjB1oF7ydJzyGfikwJcGapJsErEU4z0g781mzSDjJkaP+tBXhfAx2o45CsJOAPQKdL
36962 rosot4LKGAfmt1t06SAZf7IbiVQ=
36963 -----END CERTIFICATE-----
36964
36965 GeoTrust Primary Certification Authority - G3
36966 =============================================
36967 -----BEGIN CERTIFICATE-----
36968 MIID/jCCAuagAwIBAgIQFaxulBmyeUtB9iepwxgPHzANBgkqhkiG9w0BAQsFADCBmDELMAkGA1UE
36969 BhMCVVMxFjAUBgNVBAoTDUdlb1RydXN0IEluYy4xOTA3BgNVBAsTMChjKSAyMDA4IEdlb1RydXN0
36970 IEluYy4gLSBGb3IgYXV0aG9yaXplZCB1c2Ugb25seTE2MDQGA1UEAxMtR2VvVHJ1c3QgUHJpbWFy
36971 eSBDZXJ0aWZpY2F0aW9uIEF1dGhvcml0eSAtIEczMB4XDTA4MDQwMjAwMDAwMFoXDTM3MTIwMTIz
36972 NTk1OVowgZgxCzAJBgNVBAYTAlVTMRYwFAYDVQQKEw1HZW9UcnVzdCBJbmMuMTkwNwYDVQQLEzAo
36973 YykgMjAwOCBHZW9UcnVzdCBJbmMuIC0gRm9yIGF1dGhvcml6ZWQgdXNlIG9ubHkxNjA0BgNVBAMT
36974 LUdlb1RydXN0IFByaW1hcnkgQ2VydGlmaWNhdGlvbiBBdXRob3JpdHkgLSBHMzCCASIwDQYJKoZI
36975 hvcNAQEBBQADggEPADCCAQoCggEBANziXmJYHTNXOTIz+uvLh4yn1ErdBojqZI4xmKU4kB6Yzy5j
36976 K/BGvESyiaHAKAxJcCGVn2TAppMSAmUmhsalifD614SgcK9PGpc/BkTVyetyEH3kMSj7HGHmKAdE
36977 c5IiaacDiGydY8hS2pgn5whMcD60yRLBxWeDXTPzAxHsatBT4tG6NmCUgLthY2xbF37fQJQeqw3C
36978 IShwiP/WJmxsYAQlTlV+fe+/lEjetx3dcI0FX4ilm/LC7urRQEFtYjgdVgbFA0dRIBn8exALDmKu
36979 dlW/X3e+PkkBUz2YJQN2JFodtNuJ6nnltrM7P7pMKEF/BqxqjsHQ9gUdfeZChuOl1UcCAwEAAaNC
36980 MEAwDwYDVR0TAQH/BAUwAwEB/zAOBgNVHQ8BAf8EBAMCAQYwHQYDVR0OBBYEFMR5yo6hTgMdHNxr
36981 2zFblD4/MH8tMA0GCSqGSIb3DQEBCwUAA4IBAQAtxRPPVoB7eni9n64smefv2t+UXglpp+duaIy9
36982 cr5HqQ6XErhK8WTTOd8lNNTBzU6B8A8ExCSzNJbGpqow32hhc9f5joWJ7w5elShKKiePEI4ufIbE
36983 Ap7aDHdlDkQNkv39sxY2+hENHYwOB4lqKVb3cvTdFZx3NWZXqxNT2I7BQMXXExZacse3aQHEerGD
36984 AWh9jUGhlBjBJVz88P6DAod8DQ3PLghcSkANPuyBYeYk28rgDi0Hsj5W3I31QYUHSJsMC8tJP33s
36985 t/3LjWeJGqvtux6jAAgIFyqCXDFdRootD4abdNlF+9RAsXqqaC2Gspki4cErx5z481+oghLrGREt
36986 -----END CERTIFICATE-----
36987
36988 thawte Primary Root CA - G2
36989 ===========================
36990 -----BEGIN CERTIFICATE-----
36991 MIICiDCCAg2gAwIBAgIQNfwmXNmET8k9Jj1Xm67XVjAKBggqhkjOPQQDAzCBhDELMAkGA1UEBhMC
36992 VVMxFTATBgNVBAoTDHRoYXd0ZSwgSW5jLjE4MDYGA1UECxMvKGMpIDIwMDcgdGhhd3RlLCBJbmMu
36993 IC0gRm9yIGF1dGhvcml6ZWQgdXNlIG9ubHkxJDAiBgNVBAMTG3RoYXd0ZSBQcmltYXJ5IFJvb3Qg
36994 Q0EgLSBHMjAeFw0wNzExMDUwMDAwMDBaFw0zODAxMTgyMzU5NTlaMIGEMQswCQYDVQQGEwJVUzEV
36995 MBMGA1UEChMMdGhhd3RlLCBJbmMuMTgwNgYDVQQLEy8oYykgMjAwNyB0aGF3dGUsIEluYy4gLSBG
36996 b3IgYXV0aG9yaXplZCB1c2Ugb25seTEkMCIGA1UEAxMbdGhhd3RlIFByaW1hcnkgUm9vdCBDQSAt
36997 IEcyMHYwEAYHKoZIzj0CAQYFK4EEACIDYgAEotWcgnuVnfFSeIf+iha/BebfowJPDQfGAFG6DAJS
36998 LSKkQjnE/o/qycG+1E3/n3qe4rF8mq2nhglzh9HnmuN6papu+7qzcMBniKI11KOasf2twu8x+qi5
36999 8/sIxpHR+ymVo0IwQDAPBgNVHRMBAf8EBTADAQH/MA4GA1UdDwEB/wQEAwIBBjAdBgNVHQ4EFgQU
37000 mtgAMADna3+FGO6Lts6KDPgR4bswCgYIKoZIzj0EAwMDaQAwZgIxAN344FdHW6fmCsO99YCKlzUN
37001 G4k8VIZ3KMqh9HneteY4sPBlcIx/AlTCv//YoT7ZzwIxAMSNlPzcU9LcnXgWHxUzI1NS41oxXZ3K
37002 rr0TKUQNJ1uo52icEvdYPy5yAlejj6EULg==
37003 -----END CERTIFICATE-----
37004
37005 thawte Primary Root CA - G3
37006 ===========================
37007 -----BEGIN CERTIFICATE-----
37008 MIIEKjCCAxKgAwIBAgIQYAGXt0an6rS0mtZLL/eQ+zANBgkqhkiG9w0BAQsFADCBrjELMAkGA1UE
37009 BhMCVVMxFTATBgNVBAoTDHRoYXd0ZSwgSW5jLjEoMCYGA1UECxMfQ2VydGlmaWNhdGlvbiBTZXJ2
37010 aWNlcyBEaXZpc2lvbjE4MDYGA1UECxMvKGMpIDIwMDggdGhhd3RlLCBJbmMuIC0gRm9yIGF1dGhv
37011 cml6ZWQgdXNlIG9ubHkxJDAiBgNVBAMTG3RoYXd0ZSBQcmltYXJ5IFJvb3QgQ0EgLSBHMzAeFw0w
37012 ODA0MDIwMDAwMDBaFw0zNzEyMDEyMzU5NTlaMIGuMQswCQYDVQQGEwJVUzEVMBMGA1UEChMMdGhh
37013 d3RlLCBJbmMuMSgwJgYDVQQLEx9DZXJ0aWZpY2F0aW9uIFNlcnZpY2VzIERpdmlzaW9uMTgwNgYD
37014 VQQLEy8oYykgMjAwOCB0aGF3dGUsIEluYy4gLSBGb3IgYXV0aG9yaXplZCB1c2Ugb25seTEkMCIG
37015 A1UEAxMbdGhhd3RlIFByaW1hcnkgUm9vdCBDQSAtIEczMIIBIjANBgkqhkiG9w0BAQEFAAOCAQ8A
37016 MIIBCgKCAQEAsr8nLPvb2FvdeHsbnndmgcs+vHyu86YnmjSjaDFxODNi5PNxZnmxqWWjpYvVj2At
37017 P0LMqmsywCPLLEHd5N/8YZzic7IilRFDGF/Eth9XbAoFWCLINkw6fKXRz4aviKdEAhN0cXMKQlkC
37018 +BsUa0Lfb1+6a4KinVvnSr0eAXLbS3ToO39/fR8EtCab4LRarEc9VbjXsCZSKAExQGbY2SS99irY
37019 7CFJXJv2eul/VTV+lmuNk5Mny5K76qxAwJ/C+IDPXfRa3M50hqY+bAtTyr2SzhkGcuYMXDhpxwTW
37020 vGzOW/b3aJzcJRVIiKHpqfiYnODz1TEoYRFsZ5aNOZnLwkUkOQIDAQABo0IwQDAPBgNVHRMBAf8E
37021 BTADAQH/MA4GA1UdDwEB/wQEAwIBBjAdBgNVHQ4EFgQUrWyqlGCc7eT/+j4KdCtjA/e2Wb8wDQYJ
37022 KoZIhvcNAQELBQADggEBABpA2JVlrAmSicY59BDlqQ5mU1143vokkbvnRFHfxhY0Cu9qRFHqKweK
37023 A3rD6z8KLFIWoCtDuSWQP3CpMyVtRRooOyfPqsMpQhvfO0zAMzRbQYi/aytlryjvsvXDqmbOe1bu
37024 t8jLZ8HJnBoYuMTDSQPxYA5QzUbF83d597YV4Djbxy8ooAw/dyZ02SUS2jHaGh7cKUGRIjxpp7sC
37025 8rZcJwOJ9Abqm+RyguOhCcHpABnTPtRwa7pxpqpYrvS76Wy274fMm7v/OeZWYdMKp8RcTGB7BXcm
37026 er/YB1IsYvdwY9k5vG8cwnncdimvzsUsZAReiDZuMdRAGmI0Nj81Aa6sY6A=
37027 -----END CERTIFICATE-----
37028
37029 GeoTrust Primary Certification Authority - G2
37030 =============================================
37031 -----BEGIN CERTIFICATE-----
37032 MIICrjCCAjWgAwIBAgIQPLL0SAoA4v7rJDteYD7DazAKBggqhkjOPQQDAzCBmDELMAkGA1UEBhMC
37033 VVMxFjAUBgNVBAoTDUdlb1RydXN0IEluYy4xOTA3BgNVBAsTMChjKSAyMDA3IEdlb1RydXN0IElu
37034 Yy4gLSBGb3IgYXV0aG9yaXplZCB1c2Ugb25seTE2MDQGA1UEAxMtR2VvVHJ1c3QgUHJpbWFyeSBD
37035 ZXJ0aWZpY2F0aW9uIEF1dGhvcml0eSAtIEcyMB4XDTA3MTEwNTAwMDAwMFoXDTM4MDExODIzNTk1
37036 OVowgZgxCzAJBgNVBAYTAlVTMRYwFAYDVQQKEw1HZW9UcnVzdCBJbmMuMTkwNwYDVQQLEzAoYykg
37037 MjAwNyBHZW9UcnVzdCBJbmMuIC0gRm9yIGF1dGhvcml6ZWQgdXNlIG9ubHkxNjA0BgNVBAMTLUdl
37038 b1RydXN0IFByaW1hcnkgQ2VydGlmaWNhdGlvbiBBdXRob3JpdHkgLSBHMjB2MBAGByqGSM49AgEG
37039 BSuBBAAiA2IABBWx6P0DFUPlrOuHNxFi79KDNlJ9RVcLSo17VDs6bl8VAsBQps8lL33KSLjHUGMc
37040 KiEIfJo22Av+0SbFWDEwKCXzXV2juLaltJLtbCyf691DiaI8S0iRHVDsJt/WYC69IaNCMEAwDwYD
37041 VR0TAQH/BAUwAwEB/zAOBgNVHQ8BAf8EBAMCAQYwHQYDVR0OBBYEFBVfNVdRVfslsq0DafwBo/q+
37042 EVXVMAoGCCqGSM49BAMDA2cAMGQCMGSWWaboCd6LuvpaiIjwH5HTRqjySkwCY/tsXzjbLkGTqQ7m
37043 ndwxHLKgpxgceeHHNgIwOlavmnRs9vuD4DPTCF+hnMJbn0bWtsuRBmOiBuczrD6ogRLQy7rQkgu2
37044 npaqBA+K
37045 -----END CERTIFICATE-----
37046
37047 VeriSign Universal Root Certification Authority
37048 ===============================================
37049 -----BEGIN CERTIFICATE-----
37050 MIIEuTCCA6GgAwIBAgIQQBrEZCGzEyEDDrvkEhrFHTANBgkqhkiG9w0BAQsFADCBvTELMAkGA1UE
37051 BhMCVVMxFzAVBgNVBAoTDlZlcmlTaWduLCBJbmMuMR8wHQYDVQQLExZWZXJpU2lnbiBUcnVzdCBO
37052 ZXR3b3JrMTowOAYDVQQLEzEoYykgMjAwOCBWZXJpU2lnbiwgSW5jLiAtIEZvciBhdXRob3JpemVk
37053 IHVzZSBvbmx5MTgwNgYDVQQDEy9WZXJpU2lnbiBVbml2ZXJzYWwgUm9vdCBDZXJ0aWZpY2F0aW9u
37054 IEF1dGhvcml0eTAeFw0wODA0MDIwMDAwMDBaFw0zNzEyMDEyMzU5NTlaMIG9MQswCQYDVQQGEwJV
37055 UzEXMBUGA1UEChMOVmVyaVNpZ24sIEluYy4xHzAdBgNVBAsTFlZlcmlTaWduIFRydXN0IE5ldHdv
37056 cmsxOjA4BgNVBAsTMShjKSAyMDA4IFZlcmlTaWduLCBJbmMuIC0gRm9yIGF1dGhvcml6ZWQgdXNl
37057 IG9ubHkxODA2BgNVBAMTL1ZlcmlTaWduIFVuaXZlcnNhbCBSb290IENlcnRpZmljYXRpb24gQXV0
37058 aG9yaXR5MIIBIjANBgkqhkiG9w0BAQEFAAOCAQ8AMIIBCgKCAQEAx2E3XrEBNNti1xWb/1hajCMj
37059 1mCOkdeQmIN65lgZOIzF9uVkhbSicfvtvbnazU0AtMgtc6XHaXGVHzk8skQHnOgO+k1KxCHfKWGP
37060 MiJhgsWHH26MfF8WIFFE0XBPV+rjHOPMee5Y2A7Cs0WTwCznmhcrewA3ekEzeOEz4vMQGn+HLL72
37061 9fdC4uW/h2KJXwBL38Xd5HVEMkE6HnFuacsLdUYI0crSK5XQz/u5QGtkjFdN/BMReYTtXlT2NJ8I
37062 AfMQJQYXStrxHXpma5hgZqTZ79IugvHw7wnqRMkVauIDbjPTrJ9VAMf2CGqUuV/c4DPxhGD5WycR
37063 tPwW8rtWaoAljQIDAQABo4GyMIGvMA8GA1UdEwEB/wQFMAMBAf8wDgYDVR0PAQH/BAQDAgEGMG0G
37064 CCsGAQUFBwEMBGEwX6FdoFswWTBXMFUWCWltYWdlL2dpZjAhMB8wBwYFKw4DAhoEFI/l0xqGrI2O
37065 a8PPgGrUSBgsexkuMCUWI2h0dHA6Ly9sb2dvLnZlcmlzaWduLmNvbS92c2xvZ28uZ2lmMB0GA1Ud
37066 DgQWBBS2d/ppSEefUxLVwuoHMnYH0ZcHGTANBgkqhkiG9w0BAQsFAAOCAQEASvj4sAPmLGd75JR3
37067 Y8xuTPl9Dg3cyLk1uXBPY/ok+myDjEedO2Pzmvl2MpWRsXe8rJq+seQxIcaBlVZaDrHC1LGmWazx
37068 Y8u4TB1ZkErvkBYoH1quEPuBUDgMbMzxPcP1Y+Oz4yHJJDnp/RVmRvQbEdBNc6N9Rvk97ahfYtTx
37069 P/jgdFcrGJ2BtMQo2pSXpXDrrB2+BxHw1dvd5Yzw1TKwg+ZX4o+/vqGqvz0dtdQ46tewXDpPaj+P
37070 wGZsY6rp2aQW9IHRlRQOfc2VNNnSj3BzgXucfr2YYdhFh5iQxeuGMMY1v/D/w1WIg0vvBZIGcfK4
37071 mJO37M2CYfE45k+XmCpajQ==
37072 -----END CERTIFICATE-----
37073
37074 VeriSign Class 3 Public Primary Certification Authority - G4
37075 ============================================================
37076 -----BEGIN CERTIFICATE-----
37077 MIIDhDCCAwqgAwIBAgIQL4D+I4wOIg9IZxIokYesszAKBggqhkjOPQQDAzCByjELMAkGA1UEBhMC
37078 VVMxFzAVBgNVBAoTDlZlcmlTaWduLCBJbmMuMR8wHQYDVQQLExZWZXJpU2lnbiBUcnVzdCBOZXR3
37079 b3JrMTowOAYDVQQLEzEoYykgMjAwNyBWZXJpU2lnbiwgSW5jLiAtIEZvciBhdXRob3JpemVkIHVz
37080 ZSBvbmx5MUUwQwYDVQQDEzxWZXJpU2lnbiBDbGFzcyAzIFB1YmxpYyBQcmltYXJ5IENlcnRpZmlj
37081 YXRpb24gQXV0aG9yaXR5IC0gRzQwHhcNMDcxMTA1MDAwMDAwWhcNMzgwMTE4MjM1OTU5WjCByjEL
37082 MAkGA1UEBhMCVVMxFzAVBgNVBAoTDlZlcmlTaWduLCBJbmMuMR8wHQYDVQQLExZWZXJpU2lnbiBU
37083 cnVzdCBOZXR3b3JrMTowOAYDVQQLEzEoYykgMjAwNyBWZXJpU2lnbiwgSW5jLiAtIEZvciBhdXRo
37084 b3JpemVkIHVzZSBvbmx5MUUwQwYDVQQDEzxWZXJpU2lnbiBDbGFzcyAzIFB1YmxpYyBQcmltYXJ5
37085 IENlcnRpZmljYXRpb24gQXV0aG9yaXR5IC0gRzQwdjAQBgcqhkjOPQIBBgUrgQQAIgNiAASnVnp8
37086 Utpkmw4tXNherJI9/gHmGUo9FANL+mAnINmDiWn6VMaaGF5VKmTeBvaNSjutEDxlPZCIBIngMGGz
37087 rl0Bp3vefLK+ymVhAIau2o970ImtTR1ZmkGxvEeA3J5iw/mjgbIwga8wDwYDVR0TAQH/BAUwAwEB
37088 /zAOBgNVHQ8BAf8EBAMCAQYwbQYIKwYBBQUHAQwEYTBfoV2gWzBZMFcwVRYJaW1hZ2UvZ2lmMCEw
37089 HzAHBgUrDgMCGgQUj+XTGoasjY5rw8+AatRIGCx7GS4wJRYjaHR0cDovL2xvZ28udmVyaXNpZ24u
37090 Y29tL3ZzbG9nby5naWYwHQYDVR0OBBYEFLMWkf3upm7ktS5Jj4d4gYDs5bG1MAoGCCqGSM49BAMD
37091 A2gAMGUCMGYhDBgmYFo4e1ZC4Kf8NoRRkSAsdk1DPcQdhCPQrNZ8NQbOzWm9kA3bbEhCHQ6qQgIx
37092 AJw9SDkjOVgaFRJZap7v1VmyHVIsmXHNxynfGyphe3HR3vPA5Q06Sqotp9iGKt0uEA==
37093 -----END CERTIFICATE-----
37094
37095 NetLock Arany (Class Gold) Főtanúsítvány
37096 ============================================
37097 -----BEGIN CERTIFICATE-----
37098 MIIEFTCCAv2gAwIBAgIGSUEs5AAQMA0GCSqGSIb3DQEBCwUAMIGnMQswCQYDVQQGEwJIVTERMA8G
37099 A1UEBwwIQnVkYXBlc3QxFTATBgNVBAoMDE5ldExvY2sgS2Z0LjE3MDUGA1UECwwuVGFuw7pzw610
37100 dsOhbnlraWFkw7NrIChDZXJ0aWZpY2F0aW9uIFNlcnZpY2VzKTE1MDMGA1UEAwwsTmV0TG9jayBB
37101 cmFueSAoQ2xhc3MgR29sZCkgRsWRdGFuw7pzw610dsOhbnkwHhcNMDgxMjExMTUwODIxWhcNMjgx
37102 MjA2MTUwODIxWjCBpzELMAkGA1UEBhMCSFUxETAPBgNVBAcMCEJ1ZGFwZXN0MRUwEwYDVQQKDAxO
37103 ZXRMb2NrIEtmdC4xNzA1BgNVBAsMLlRhbsO6c8OtdHbDoW55a2lhZMOzayAoQ2VydGlmaWNhdGlv
37104 biBTZXJ2aWNlcykxNTAzBgNVBAMMLE5ldExvY2sgQXJhbnkgKENsYXNzIEdvbGQpIEbFkXRhbsO6
37105 c8OtdHbDoW55MIIBIjANBgkqhkiG9w0BAQEFAAOCAQ8AMIIBCgKCAQEAxCRec75LbRTDofTjl5Bu
37106 0jBFHjzuZ9lk4BqKf8owyoPjIMHj9DrTlF8afFttvzBPhCf2nx9JvMaZCpDyD/V/Q4Q3Y1GLeqVw
37107 /HpYzY6b7cNGbIRwXdrzAZAj/E4wqX7hJ2Pn7WQ8oLjJM2P+FpD/sLj916jAwJRDC7bVWaaeVtAk
37108 H3B5r9s5VA1lddkVQZQBr17s9o3x/61k/iCa11zr/qYfCGSji3ZVrR47KGAuhyXoqq8fxmRGILdw
37109 fzzeSNuWU7c5d+Qa4scWhHaXWy+7GRWF+GmF9ZmnqfI0p6m2pgP8b4Y9VHx2BJtr+UBdADTHLpl1
37110 neWIA6pN+APSQnbAGwIDAKiLo0UwQzASBgNVHRMBAf8ECDAGAQH/AgEEMA4GA1UdDwEB/wQEAwIB
37111 BjAdBgNVHQ4EFgQUzPpnk/C2uNClwB7zU/2MU9+D15YwDQYJKoZIhvcNAQELBQADggEBAKt/7hwW
37112 qZw8UQCgwBEIBaeZ5m8BiFRhbvG5GK1Krf6BQCOUL/t1fC8oS2IkgYIL9WHxHG64YTjrgfpioTta
37113 YtOUZcTh5m2C+C8lcLIhJsFyUR+MLMOEkMNaj7rP9KdlpeuY0fsFskZ1FSNqb4VjMIDw1Z4fKRzC
37114 bLBQWV2QWzuoDTDPv31/zvGdg73JRm4gpvlhUbohL3u+pRVjodSVh/GeufOJ8z2FuLjbvrW5Kfna
37115 NwUASZQDhETnv0Mxz3WLJdH0pmT1kvarBes96aULNmLazAZfNou2XjG4Kvte9nHfRCaexOYNkbQu
37116 dZWAUWpLMKawYqGT8ZvYzsRjdT9ZR7E=
37117 -----END CERTIFICATE-----
37118
37119 Staat der Nederlanden Root CA - G2
37120 ==================================
37121 -----BEGIN CERTIFICATE-----
37122 MIIFyjCCA7KgAwIBAgIEAJiWjDANBgkqhkiG9w0BAQsFADBaMQswCQYDVQQGEwJOTDEeMBwGA1UE
37123 CgwVU3RhYXQgZGVyIE5lZGVybGFuZGVuMSswKQYDVQQDDCJTdGFhdCBkZXIgTmVkZXJsYW5kZW4g
37124 Um9vdCBDQSAtIEcyMB4XDTA4MDMyNjExMTgxN1oXDTIwMDMyNTExMDMxMFowWjELMAkGA1UEBhMC
37125 TkwxHjAcBgNVBAoMFVN0YWF0IGRlciBOZWRlcmxhbmRlbjErMCkGA1UEAwwiU3RhYXQgZGVyIE5l
37126 ZGVybGFuZGVuIFJvb3QgQ0EgLSBHMjCCAiIwDQYJKoZIhvcNAQEBBQADggIPADCCAgoCggIBAMVZ
37127 5291qj5LnLW4rJ4L5PnZyqtdj7U5EILXr1HgO+EASGrP2uEGQxGZqhQlEq0i6ABtQ8SpuOUfiUtn
37128 vWFI7/3S4GCI5bkYYCjDdyutsDeqN95kWSpGV+RLufg3fNU254DBtvPUZ5uW6M7XxgpT0GtJlvOj
37129 CwV3SPcl5XCsMBQgJeN/dVrlSPhOewMHBPqCYYdu8DvEpMfQ9XQ+pV0aCPKbJdL2rAQmPlU6Yiil
37130 e7Iwr/g3wtG61jj99O9JMDeZJiFIhQGp5Rbn3JBV3w/oOM2ZNyFPXfUib2rFEhZgF1XyZWampzCR
37131 OME4HYYEhLoaJXhena/MUGDWE4dS7WMfbWV9whUYdMrhfmQpjHLYFhN9C0lK8SgbIHRrxT3dsKpI
37132 CT0ugpTNGmXZK4iambwYfp/ufWZ8Pr2UuIHOzZgweMFvZ9C+X+Bo7d7iscksWXiSqt8rYGPy5V65
37133 48r6f1CGPqI0GAwJaCgRHOThuVw+R7oyPxjMW4T182t0xHJ04eOLoEq9jWYv6q012iDTiIJh8BIi
37134 trzQ1aTsr1SIJSQ8p22xcik/Plemf1WvbibG/ufMQFxRRIEKeN5KzlW/HdXZt1bv8Hb/C3m1r737
37135 qWmRRpdogBQ2HbN/uymYNqUg+oJgYjOk7Na6B6duxc8UpufWkjTYgfX8HV2qXB72o007uPc5AgMB
37136 AAGjgZcwgZQwDwYDVR0TAQH/BAUwAwEB/zBSBgNVHSAESzBJMEcGBFUdIAAwPzA9BggrBgEFBQcC
37137 ARYxaHR0cDovL3d3dy5wa2lvdmVyaGVpZC5ubC9wb2xpY2llcy9yb290LXBvbGljeS1HMjAOBgNV
37138 HQ8BAf8EBAMCAQYwHQYDVR0OBBYEFJFoMocVHYnitfGsNig0jQt8YojrMA0GCSqGSIb3DQEBCwUA
37139 A4ICAQCoQUpnKpKBglBu4dfYszk78wIVCVBR7y29JHuIhjv5tLySCZa59sCrI2AGeYwRTlHSeYAz
37140 +51IvuxBQ4EffkdAHOV6CMqqi3WtFMTC6GY8ggen5ieCWxjmD27ZUD6KQhgpxrRW/FYQoAUXvQwj
37141 f/ST7ZwaUb7dRUG/kSS0H4zpX897IZmflZ85OkYcbPnNe5yQzSipx6lVu6xiNGI1E0sUOlWDuYaN
37142 kqbG9AclVMwWVxJKgnjIFNkXgiYtXSAfea7+1HAWFpWD2DU5/1JddRwWxRNVz0fMdWVSSt7wsKfk
37143 CpYL+63C4iWEst3kvX5ZbJvw8NjnyvLplzh+ib7M+zkXYT9y2zqR2GUBGR2tUKRXCnxLvJxxcypF
37144 URmFzI79R6d0lR2o0a9OF7FpJsKqeFdbxU2n5Z4FF5TKsl+gSRiNNOkmbEgeqmiSBeGCc1qb3Adb
37145 CG19ndeNIdn8FCCqwkXfP+cAslHkwvgFuXkajDTznlvkN1trSt8sV4pAWja63XVECDdCcAz+3F4h
37146 oKOKwJCcaNpQ5kUQR3i2TtJlycM33+FCY7BXN0Ute4qcvwXqZVUz9zkQxSgqIXobisQk+T8VyJoV
37147 IPVVYpbtbZNQvOSqeK3Zywplh6ZmwcSBo3c6WB4L7oOLnR7SUqTMHW+wmG2UMbX4cQrcufx9MmDm
37148 66+KAQ==
37149 -----END CERTIFICATE-----
37150
37151 CA Disig
37152 ========
37153 -----BEGIN CERTIFICATE-----
37154 MIIEDzCCAvegAwIBAgIBATANBgkqhkiG9w0BAQUFADBKMQswCQYDVQQGEwJTSzETMBEGA1UEBxMK
37155 QnJhdGlzbGF2YTETMBEGA1UEChMKRGlzaWcgYS5zLjERMA8GA1UEAxMIQ0EgRGlzaWcwHhcNMDYw
37156 MzIyMDEzOTM0WhcNMTYwMzIyMDEzOTM0WjBKMQswCQYDVQQGEwJTSzETMBEGA1UEBxMKQnJhdGlz
37157 bGF2YTETMBEGA1UEChMKRGlzaWcgYS5zLjERMA8GA1UEAxMIQ0EgRGlzaWcwggEiMA0GCSqGSIb3
37158 DQEBAQUAA4IBDwAwggEKAoIBAQCS9jHBfYj9mQGp2HvycXXxMcbzdWb6UShGhJd4NLxs/LxFWYgm
37159 GErENx+hSkS943EE9UQX4j/8SFhvXJ56CbpRNyIjZkMhsDxkovhqFQ4/61HhVKndBpnXmjxUizkD
37160 Pw/Fzsbrg3ICqB9x8y34dQjbYkzo+s7552oftms1grrijxaSfQUMbEYDXcDtab86wYqg6I7ZuUUo
37161 hwjstMoVvoLdtUSLLa2GDGhibYVW8qwUYzrG0ZmsNHhWS8+2rT+MitcE5eN4TPWGqvWP+j1scaMt
37162 ymfraHtuM6kMgiioTGohQBUgDCZbg8KpFhXAJIJdKxatymP2dACw30PEEGBWZ2NFAgMBAAGjgf8w
37163 gfwwDwYDVR0TAQH/BAUwAwEB/zAdBgNVHQ4EFgQUjbJJaJ1yCCW5wCf1UJNWSEZx+Y8wDgYDVR0P
37164 AQH/BAQDAgEGMDYGA1UdEQQvMC2BE2Nhb3BlcmF0b3JAZGlzaWcuc2uGFmh0dHA6Ly93d3cuZGlz
37165 aWcuc2svY2EwZgYDVR0fBF8wXTAtoCugKYYnaHR0cDovL3d3dy5kaXNpZy5zay9jYS9jcmwvY2Ff
37166 ZGlzaWcuY3JsMCygKqAohiZodHRwOi8vY2EuZGlzaWcuc2svY2EvY3JsL2NhX2Rpc2lnLmNybDAa
37167 BgNVHSAEEzARMA8GDSuBHpGT5goAAAABAQEwDQYJKoZIhvcNAQEFBQADggEBAF00dGFMrzvY/59t
37168 WDYcPQuBDRIrRhCA/ec8J9B6yKm2fnQwM6M6int0wHl5QpNt/7EpFIKrIYwvF/k/Ji/1WcbvgAa3
37169 mkkp7M5+cTxqEEHA9tOasnxakZzArFvITV734VP/Q3f8nktnbNfzg9Gg4H8l37iYC5oyOGwwoPP/
37170 CBUz91BKez6jPiCp3C9WgArtQVCwyfTssuMmRAAOb54GvCKWU3BlxFAKRmukLyeBEicTXxChds6K
37171 ezfqwzlhA5WYOudsiCUI/HloDYd9Yvi0X/vF2Ey9WLw/Q1vUHgFNPGO+I++MzVpQuGhU+QqZMxEA
37172 4Z7CRneC9VkGjCFMhwnN5ag=
37173 -----END CERTIFICATE-----
37174
37175 Juur-SK
37176 =======
37177 -----BEGIN CERTIFICATE-----
37178 MIIE5jCCA86gAwIBAgIEO45L/DANBgkqhkiG9w0BAQUFADBdMRgwFgYJKoZIhvcNAQkBFglwa2lA
37179 c2suZWUxCzAJBgNVBAYTAkVFMSIwIAYDVQQKExlBUyBTZXJ0aWZpdHNlZXJpbWlza2Vza3VzMRAw
37180 DgYDVQQDEwdKdXVyLVNLMB4XDTAxMDgzMDE0MjMwMVoXDTE2MDgyNjE0MjMwMVowXTEYMBYGCSqG
37181 SIb3DQEJARYJcGtpQHNrLmVlMQswCQYDVQQGEwJFRTEiMCAGA1UEChMZQVMgU2VydGlmaXRzZWVy
37182 aW1pc2tlc2t1czEQMA4GA1UEAxMHSnV1ci1TSzCCASIwDQYJKoZIhvcNAQEBBQADggEPADCCAQoC
37183 ggEBAIFxNj4zB9bjMI0TfncyRsvPGbJgMUaXhvSYRqTCZUXP00B841oiqBB4M8yIsdOBSvZiF3tf
37184 TQou0M+LI+5PAk676w7KvRhj6IAcjeEcjT3g/1tf6mTll+g/mX8MCgkzABpTpyHhOEvWgxutr2TC
37185 +Rx6jGZITWYfGAriPrsfB2WThbkasLnE+w0R9vXW+RvHLCu3GFH+4Hv2qEivbDtPL+/40UceJlfw
37186 UR0zlv/vWT3aTdEVNMfqPxZIe5EcgEMPPbgFPtGzlc3Yyg/CQ2fbt5PgIoIuvvVoKIO5wTtpeyDa
37187 Tpxt4brNj3pssAki14sL2xzVWiZbDcDq5WDQn/413z8CAwEAAaOCAawwggGoMA8GA1UdEwEB/wQF
37188 MAMBAf8wggEWBgNVHSAEggENMIIBCTCCAQUGCisGAQQBzh8BAQEwgfYwgdAGCCsGAQUFBwICMIHD
37189 HoHAAFMAZQBlACAAcwBlAHIAdABpAGYAaQBrAGEAYQB0ACAAbwBuACAAdgDkAGwAagBhAHMAdABh
37190 AHQAdQBkACAAQQBTAC0AaQBzACAAUwBlAHIAdABpAGYAaQB0AHMAZQBlAHIAaQBtAGkAcwBrAGUA
37191 cwBrAHUAcwAgAGEAbABhAG0ALQBTAEsAIABzAGUAcgB0AGkAZgBpAGsAYQBhAHQAaQBkAGUAIABr
37192 AGkAbgBuAGkAdABhAG0AaQBzAGUAawBzMCEGCCsGAQUFBwIBFhVodHRwOi8vd3d3LnNrLmVlL2Nw
37193 cy8wKwYDVR0fBCQwIjAgoB6gHIYaaHR0cDovL3d3dy5zay5lZS9qdXVyL2NybC8wHQYDVR0OBBYE
37194 FASqekej5ImvGs8KQKcYP2/v6X2+MB8GA1UdIwQYMBaAFASqekej5ImvGs8KQKcYP2/v6X2+MA4G
37195 A1UdDwEB/wQEAwIB5jANBgkqhkiG9w0BAQUFAAOCAQEAe8EYlFOiCfP+JmeaUOTDBS8rNXiRTHyo
37196 ERF5TElZrMj3hWVcRrs7EKACr81Ptcw2Kuxd/u+gkcm2k298gFTsxwhwDY77guwqYHhpNjbRxZyL
37197 abVAyJRld/JXIWY7zoVAtjNjGr95HvxcHdMdkxuLDF2FvZkwMhgJkVLpfKG6/2SSmuz+Ne6ML678
37198 IIbsSt4beDI3poHSna9aEhbKmVv8b20OxaAehsmR0FyYgl9jDIpaq9iVpszLita/ZEuOyoqysOkh
37199 Mp6qqIWYNIE5ITuoOlIyPfZrN4YGWhWY3PARZv40ILcD9EEQfTmEeZZyY7aWAuVrua0ZTbvGRNs2
37200 yyqcjg==
37201 -----END CERTIFICATE-----
37202
37203 Hongkong Post Root CA 1
37204 =======================
37205 -----BEGIN CERTIFICATE-----
37206 MIIDMDCCAhigAwIBAgICA+gwDQYJKoZIhvcNAQEFBQAwRzELMAkGA1UEBhMCSEsxFjAUBgNVBAoT
37207 DUhvbmdrb25nIFBvc3QxIDAeBgNVBAMTF0hvbmdrb25nIFBvc3QgUm9vdCBDQSAxMB4XDTAzMDUx
37208 NTA1MTMxNFoXDTIzMDUxNTA0NTIyOVowRzELMAkGA1UEBhMCSEsxFjAUBgNVBAoTDUhvbmdrb25n
37209 IFBvc3QxIDAeBgNVBAMTF0hvbmdrb25nIFBvc3QgUm9vdCBDQSAxMIIBIjANBgkqhkiG9w0BAQEF
37210 AAOCAQ8AMIIBCgKCAQEArP84tulmAknjorThkPlAj3n54r15/gK97iSSHSL22oVyaf7XPwnU3ZG1
37211 ApzQjVrhVcNQhrkpJsLj2aDxaQMoIIBFIi1WpztUlVYiWR8o3x8gPW2iNr4joLFutbEnPzlTCeqr
37212 auh0ssJlXI6/fMN4hM2eFvz1Lk8gKgifd/PFHsSaUmYeSF7jEAaPIpjhZY4bXSNmO7ilMlHIhqqh
37213 qZ5/dpTCpmy3QfDVyAY45tQM4vM7TG1QjMSDJ8EThFk9nnV0ttgCXjqQesBCNnLsak3c78QA3xMY
37214 V18meMjWCnl3v/evt3a5pQuEF10Q6m/hq5URX208o1xNg1vysxmKgIsLhwIDAQABoyYwJDASBgNV
37215 HRMBAf8ECDAGAQH/AgEDMA4GA1UdDwEB/wQEAwIBxjANBgkqhkiG9w0BAQUFAAOCAQEADkbVPK7i
37216 h9legYsCmEEIjEy82tvuJxuC52pF7BaLT4Wg87JwvVqWuspube5Gi27nKi6Wsxkz67SfqLI37pio
37217 l7Yutmcn1KZJ/RyTZXaeQi/cImyaT/JaFTmxcdcrUehtHJjA2Sr0oYJ71clBoiMBdDhViw+5Lmei
37218 IAQ32pwL0xch4I+XeTRvhEgCIDMb5jREn5Fw9IBehEPCKdJsEhTkYY2sEJCehFC78JZvRZ+K88ps
37219 T/oROhUVRsPNH4NbLUES7VBnQRM9IauUiqpOfMGx+6fWtScvl6tu4B3i0RwsH0Ti/L6RoZz71ilT
37220 c4afU9hDDl3WY4JxHYB0yvbiAmvZWg==
37221 -----END CERTIFICATE-----
37222
37223 SecureSign RootCA11
37224 ===================
37225 -----BEGIN CERTIFICATE-----
37226 MIIDbTCCAlWgAwIBAgIBATANBgkqhkiG9w0BAQUFADBYMQswCQYDVQQGEwJKUDErMCkGA1UEChMi
37227 SmFwYW4gQ2VydGlmaWNhdGlvbiBTZXJ2aWNlcywgSW5jLjEcMBoGA1UEAxMTU2VjdXJlU2lnbiBS
37228 b290Q0ExMTAeFw0wOTA0MDgwNDU2NDdaFw0yOTA0MDgwNDU2NDdaMFgxCzAJBgNVBAYTAkpQMSsw
37229 KQYDVQQKEyJKYXBhbiBDZXJ0aWZpY2F0aW9uIFNlcnZpY2VzLCBJbmMuMRwwGgYDVQQDExNTZWN1
37230 cmVTaWduIFJvb3RDQTExMIIBIjANBgkqhkiG9w0BAQEFAAOCAQ8AMIIBCgKCAQEA/XeqpRyQBTvL
37231 TJszi1oURaTnkBbR31fSIRCkF/3frNYfp+TbfPfs37gD2pRY/V1yfIw/XwFndBWW4wI8h9uuywGO
37232 wvNmxoVF9ALGOrVisq/6nL+k5tSAMJjzDbaTj6nU2DbysPyKyiyhFTOVMdrAG/LuYpmGYz+/3ZMq
37233 g6h2uRMft85OQoWPIucuGvKVCbIFtUROd6EgvanyTgp9UK31BQ1FT0Zx/Sg+U/sE2C3XZR1KG/rP
37234 O7AxmjVuyIsG0wCR8pQIZUyxNAYAeoni8McDWc/V1uinMrPmmECGxc0nEovMe863ETxiYAcjPitA
37235 bpSACW22s293bzUIUPsCh8U+iQIDAQABo0IwQDAdBgNVHQ4EFgQUW/hNT7KlhtQ60vFjmqC+CfZX
37236 t94wDgYDVR0PAQH/BAQDAgEGMA8GA1UdEwEB/wQFMAMBAf8wDQYJKoZIhvcNAQEFBQADggEBAKCh
37237 OBZmLqdWHyGcBvod7bkixTgm2E5P7KN/ed5GIaGHd48HCJqypMWvDzKYC3xmKbabfSVSSUOrTC4r
37238 bnpwrxYO4wJs+0LmGJ1F2FXI6Dvd5+H0LgscNFxsWEr7jIhQX5Ucv+2rIrVls4W6ng+4reV6G4pQ
37239 Oh29Dbx7VFALuUKvVaAYga1lme++5Jy/xIWrQbJUb9wlze144o4MjQlJ3WN7WmmWAiGovVJZ6X01
37240 y8hSyn+B/tlr0/cR7SXf+Of5pPpyl4RTDaXQMhhRdlkUbA/r7F+AjHVDg8OFmP9Mni0N5HeDk061
37241 lgeLKBObjBmNQSdJQO7e5iNEOdyhIta6A/I=
37242 -----END CERTIFICATE-----
37243
37244 ACEDICOM Root
37245 =============
37246 -----BEGIN CERTIFICATE-----
37247 MIIFtTCCA52gAwIBAgIIYY3HhjsBggUwDQYJKoZIhvcNAQEFBQAwRDEWMBQGA1UEAwwNQUNFRElD
37248 T00gUm9vdDEMMAoGA1UECwwDUEtJMQ8wDQYDVQQKDAZFRElDT00xCzAJBgNVBAYTAkVTMB4XDTA4
37249 MDQxODE2MjQyMloXDTI4MDQxMzE2MjQyMlowRDEWMBQGA1UEAwwNQUNFRElDT00gUm9vdDEMMAoG
37250 A1UECwwDUEtJMQ8wDQYDVQQKDAZFRElDT00xCzAJBgNVBAYTAkVTMIICIjANBgkqhkiG9w0BAQEF
37251 AAOCAg8AMIICCgKCAgEA/5KV4WgGdrQsyFhIyv2AVClVYyT/kGWbEHV7w2rbYgIB8hiGtXxaOLHk
37252 WLn709gtn70yN78sFW2+tfQh0hOR2QetAQXW8713zl9CgQr5auODAKgrLlUTY4HKRxx7XBZXehuD
37253 YAQ6PmXDzQHe3qTWDLqO3tkE7hdWIpuPY/1NFgu3e3eM+SW10W2ZEi5PGrjm6gSSrj0RuVFCPYew
37254 MYWveVqc/udOXpJPQ/yrOq2lEiZmueIM15jO1FillUAKt0SdE3QrwqXrIhWYENiLxQSfHY9g5QYb
37255 m8+5eaA9oiM/Qj9r+hwDezCNzmzAv+YbX79nuIQZ1RXve8uQNjFiybwCq0Zfm/4aaJQ0PZCOrfbk
37256 HQl/Sog4P75n/TSW9R28MHTLOO7VbKvU/PQAtwBbhTIWdjPp2KOZnQUAqhbm84F9b32qhm2tFXTT
37257 xKJxqvQUfecyuB+81fFOvW8XAjnXDpVCOscAPukmYxHqC9FK/xidstd7LzrZlvvoHpKuE1XI2Sf2
37258 3EgbsCTBheN3nZqk8wwRHQ3ItBTutYJXCb8gWH8vIiPYcMt5bMlL8qkqyPyHK9caUPgn6C9D4zq9
37259 2Fdx/c6mUlv53U3t5fZvie27k5x2IXXwkkwp9y+cAS7+UEaeZAwUswdbxcJzbPEHXEUkFDWug/Fq
37260 TYl6+rPYLWbwNof1K1MCAwEAAaOBqjCBpzAPBgNVHRMBAf8EBTADAQH/MB8GA1UdIwQYMBaAFKaz
37261 4SsrSbbXc6GqlPUB53NlTKxQMA4GA1UdDwEB/wQEAwIBhjAdBgNVHQ4EFgQUprPhKytJttdzoaqU
37262 9QHnc2VMrFAwRAYDVR0gBD0wOzA5BgRVHSAAMDEwLwYIKwYBBQUHAgEWI2h0dHA6Ly9hY2VkaWNv
37263 bS5lZGljb21ncm91cC5jb20vZG9jMA0GCSqGSIb3DQEBBQUAA4ICAQDOLAtSUWImfQwng4/F9tqg
37264 aHtPkl7qpHMyEVNEskTLnewPeUKzEKbHDZ3Ltvo/Onzqv4hTGzz3gvoFNTPhNahXwOf9jU8/kzJP
37265 eGYDdwdY6ZXIfj7QeQCM8htRM5u8lOk6e25SLTKeI6RF+7YuE7CLGLHdztUdp0J/Vb77W7tH1Pwk
37266 zQSulgUV1qzOMPPKC8W64iLgpq0i5ALudBF/TP94HTXa5gI06xgSYXcGCRZj6hitoocf8seACQl1
37267 ThCojz2GuHURwCRiipZ7SkXp7FnFvmuD5uHorLUwHv4FB4D54SMNUI8FmP8sX+g7tq3PgbUhh8oI
37268 KiMnMCArz+2UW6yyetLHKKGKC5tNSixthT8Jcjxn4tncB7rrZXtaAWPWkFtPF2Y9fwsZo5NjEFIq
37269 nxQWWOLcpfShFosOkYuByptZ+thrkQdlVV9SH686+5DdaaVbnG0OLLb6zqylfDJKZ0DcMDQj3dcE
37270 I2bw/FWAp/tmGYI1Z2JwOV5vx+qQQEQIHriy1tvuWacNGHk0vFQYXlPKNFHtRQrmjseCNj6nOGOp
37271 MCwXEGCSn1WHElkQwg9naRHMTh5+Spqtr0CodaxWkHS4oJyleW/c6RrIaQXpuvoDs3zk4E7Czp3o
37272 tkYNbn5XOmeUwssfnHdKZ05phkOTOPu220+DkdRgfks+KzgHVZhepA==
37273 -----END CERTIFICATE-----
37274
37275 Verisign Class 1 Public Primary Certification Authority
37276 =======================================================
37277 -----BEGIN CERTIFICATE-----
37278 MIICPDCCAaUCED9pHoGc8JpK83P/uUii5N0wDQYJKoZIhvcNAQEFBQAwXzELMAkGA1UEBhMCVVMx
37279 FzAVBgNVBAoTDlZlcmlTaWduLCBJbmMuMTcwNQYDVQQLEy5DbGFzcyAxIFB1YmxpYyBQcmltYXJ5
37280 IENlcnRpZmljYXRpb24gQXV0aG9yaXR5MB4XDTk2MDEyOTAwMDAwMFoXDTI4MDgwMjIzNTk1OVow
37281 XzELMAkGA1UEBhMCVVMxFzAVBgNVBAoTDlZlcmlTaWduLCBJbmMuMTcwNQYDVQQLEy5DbGFzcyAx
37282 IFB1YmxpYyBQcmltYXJ5IENlcnRpZmljYXRpb24gQXV0aG9yaXR5MIGfMA0GCSqGSIb3DQEBAQUA
37283 A4GNADCBiQKBgQDlGb9to1ZhLZlIcfZn3rmN67eehoAKkQ76OCWvRoiC5XOooJskXQ0fzGVuDLDQ
37284 VoQYh5oGmxChc9+0WDlrbsH2FdWoqD+qEgaNMax/sDTXjzRniAnNFBHiTkVWaR94AoDa3EeRKbs2
37285 yWNcxeDXLYd7obcysHswuiovMaruo2fa2wIDAQABMA0GCSqGSIb3DQEBBQUAA4GBAFgVKTk8d6Pa
37286 XCUDfGD67gmZPCcQcMgMCeazh88K4hiWNWLMv5sneYlfycQJ9M61Hd8qveXbhpxoJeUwfLaJFf5n
37287 0a3hUKw8fGJLj7qE1xIVGx/KXQ/BUpQqEZnae88MNhPVNdwQGVnqlMEAv3WP2fr9dgTbYruQagPZ
37288 RjXZ+Hxb
37289 -----END CERTIFICATE-----
37290
37291 Verisign Class 3 Public Primary Certification Authority
37292 =======================================================
37293 -----BEGIN CERTIFICATE-----
37294 MIICPDCCAaUCEDyRMcsf9tAbDpq40ES/Er4wDQYJKoZIhvcNAQEFBQAwXzELMAkGA1UEBhMCVVMx
37295 FzAVBgNVBAoTDlZlcmlTaWduLCBJbmMuMTcwNQYDVQQLEy5DbGFzcyAzIFB1YmxpYyBQcmltYXJ5
37296 IENlcnRpZmljYXRpb24gQXV0aG9yaXR5MB4XDTk2MDEyOTAwMDAwMFoXDTI4MDgwMjIzNTk1OVow
37297 XzELMAkGA1UEBhMCVVMxFzAVBgNVBAoTDlZlcmlTaWduLCBJbmMuMTcwNQYDVQQLEy5DbGFzcyAz
37298 IFB1YmxpYyBQcmltYXJ5IENlcnRpZmljYXRpb24gQXV0aG9yaXR5MIGfMA0GCSqGSIb3DQEBAQUA
37299 A4GNADCBiQKBgQDJXFme8huKARS0EN8EQNvjV69qRUCPhAwL0TPZ2RHP7gJYHyX3KqhEBarsAx94
37300 f56TuZoAqiN91qyFomNFx3InzPRMxnVx0jnvT0Lwdd8KkMaOIG+YD/isI19wKTakyYbnsZogy1Ol
37301 hec9vn2a/iRFM9x2Fe0PonFkTGUugWhFpwIDAQABMA0GCSqGSIb3DQEBBQUAA4GBABByUqkFFBky
37302 CEHwxWsKzH4PIRnN5GfcX6kb5sroc50i2JhucwNhkcV8sEVAbkSdjbCxlnRhLQ2pRdKkkirWmnWX
37303 bj9T/UWZYB2oK0z5XqcJ2HUw19JlYD1n1khVdWk/kfVIC0dpImmClr7JyDiGSnoscxlIaU5rfGW/
37304 D/xwzoiQ
37305 -----END CERTIFICATE-----
37306
37307 Microsec e-Szigno Root CA 2009
37308 ==============================
37309 -----BEGIN CERTIFICATE-----
37310 MIIECjCCAvKgAwIBAgIJAMJ+QwRORz8ZMA0GCSqGSIb3DQEBCwUAMIGCMQswCQYDVQQGEwJIVTER
37311 MA8GA1UEBwwIQnVkYXBlc3QxFjAUBgNVBAoMDU1pY3Jvc2VjIEx0ZC4xJzAlBgNVBAMMHk1pY3Jv
37312 c2VjIGUtU3ppZ25vIFJvb3QgQ0EgMjAwOTEfMB0GCSqGSIb3DQEJARYQaW5mb0BlLXN6aWduby5o
37313 dTAeFw0wOTA2MTYxMTMwMThaFw0yOTEyMzAxMTMwMThaMIGCMQswCQYDVQQGEwJIVTERMA8GA1UE
37314 BwwIQnVkYXBlc3QxFjAUBgNVBAoMDU1pY3Jvc2VjIEx0ZC4xJzAlBgNVBAMMHk1pY3Jvc2VjIGUt
37315 U3ppZ25vIFJvb3QgQ0EgMjAwOTEfMB0GCSqGSIb3DQEJARYQaW5mb0BlLXN6aWduby5odTCCASIw
37316 DQYJKoZIhvcNAQEBBQADggEPADCCAQoCggEBAOn4j/NjrdqG2KfgQvvPkd6mJviZpWNwrZuuyjNA
37317 fW2WbqEORO7hE52UQlKavXWFdCyoDh2Tthi3jCyoz/tccbna7P7ofo/kLx2yqHWH2Leh5TvPmUpG
37318 0IMZfcChEhyVbUr02MelTTMuhTlAdX4UfIASmFDHQWe4oIBhVKZsTh/gnQ4H6cm6M+f+wFUoLAKA
37319 pxn1ntxVUwOXewdI/5n7N4okxFnMUBBjjqqpGrCEGob5X7uxUG6k0QrM1XF+H6cbfPVTbiJfyyvm
37320 1HxdrtbCxkzlBQHZ7Vf8wSN5/PrIJIOV87VqUQHQd9bpEqH5GoP7ghu5sJf0dgYzQ0mg/wu1+rUC
37321 AwEAAaOBgDB+MA8GA1UdEwEB/wQFMAMBAf8wDgYDVR0PAQH/BAQDAgEGMB0GA1UdDgQWBBTLD8bf
37322 QkPMPcu1SCOhGnqmKrs0aDAfBgNVHSMEGDAWgBTLD8bfQkPMPcu1SCOhGnqmKrs0aDAbBgNVHREE
37323 FDASgRBpbmZvQGUtc3ppZ25vLmh1MA0GCSqGSIb3DQEBCwUAA4IBAQDJ0Q5eLtXMs3w+y/w9/w0o
37324 lZMEyL/azXm4Q5DwpL7v8u8hmLzU1F0G9u5C7DBsoKqpyvGvivo/C3NqPuouQH4frlRheesuCDfX
37325 I/OMn74dseGkddug4lQUsbocKaQY9hK6ohQU4zE1yED/t+AFdlfBHFny+L/k7SViXITwfn4fs775
37326 tyERzAMBVnCnEJIeGzSBHq2cGsMEPO0CYdYeBvNfOofyK/FFh+U9rNHHV4S9a67c2Pm2G2JwCz02
37327 yULyMtd6YebS2z3PyKnJm9zbWETXbzivf3jTo60adbocwTZ8jx5tHMN1Rq41Bab2XD0h7lbwyYIi
37328 LXpUq3DDfSJlgnCW
37329 -----END CERTIFICATE-----
37330
37331 E-Guven Kok Elektronik Sertifika Hizmet Saglayicisi
37332 ===================================================
37333 -----BEGIN CERTIFICATE-----
37334 MIIDtjCCAp6gAwIBAgIQRJmNPMADJ72cdpW56tustTANBgkqhkiG9w0BAQUFADB1MQswCQYDVQQG
37335 EwJUUjEoMCYGA1UEChMfRWxla3Ryb25payBCaWxnaSBHdXZlbmxpZ2kgQS5TLjE8MDoGA1UEAxMz
37336 ZS1HdXZlbiBLb2sgRWxla3Ryb25payBTZXJ0aWZpa2EgSGl6bWV0IFNhZ2xheWljaXNpMB4XDTA3
37337 MDEwNDExMzI0OFoXDTE3MDEwNDExMzI0OFowdTELMAkGA1UEBhMCVFIxKDAmBgNVBAoTH0VsZWt0
37338 cm9uaWsgQmlsZ2kgR3V2ZW5saWdpIEEuUy4xPDA6BgNVBAMTM2UtR3V2ZW4gS29rIEVsZWt0cm9u
37339 aWsgU2VydGlmaWthIEhpem1ldCBTYWdsYXlpY2lzaTCCASIwDQYJKoZIhvcNAQEBBQADggEPADCC
37340 AQoCggEBAMMSIJ6wXgBljU5Gu4Bc6SwGl9XzcslwuedLZYDBS75+PNdUMZTe1RK6UxYC6lhj71vY
37341 8+0qGqpxSKPcEC1fX+tcS5yWCEIlKBHMilpiAVDV6wlTL/jDj/6z/P2douNffb7tC+Bg62nsM+3Y
37342 jfsSSYMAyYuXjDtzKjKzEve5TfL0TW3H5tYmNwjy2f1rXKPlSFxYvEK+A1qBuhw1DADT9SN+cTAI
37343 JjjcJRFHLfO6IxClv7wC90Nex/6wN1CZew+TzuZDLMN+DfIcQ2Zgy2ExR4ejT669VmxMvLz4Bcpk
37344 9Ok0oSy1c+HCPujIyTQlCFzz7abHlJ+tiEMl1+E5YP6sOVkCAwEAAaNCMEAwDgYDVR0PAQH/BAQD
37345 AgEGMA8GA1UdEwEB/wQFMAMBAf8wHQYDVR0OBBYEFJ/uRLOU1fqRTy7ZVZoEVtstxNulMA0GCSqG
37346 SIb3DQEBBQUAA4IBAQB/X7lTW2M9dTLn+sR0GstG30ZpHFLPqk/CaOv/gKlR6D1id4k9CnU58W5d
37347 F4dvaAXBlGzZXd/aslnLpRCKysw5zZ/rTt5S/wzw9JKp8mxTq5vSR6AfdPebmvEvFZ96ZDAYBzwq
37348 D2fK/A+JYZ1lpTzlvBNbCNvj/+27BrtqBrF6T2XGgv0enIu1De5Iu7i9qgi0+6N8y5/NkHZchpZ4
37349 Vwpm+Vganf2XKWDeEaaQHBkc7gGWIjQ0LpH5t8Qn0Xvmv/uARFoW5evg1Ao4vOSR49XrXMGs3xtq
37350 fJ7lddK2l4fbzIcrQzqECK+rPNv3PGYxhrCdU3nt+CPeQuMtgvEP5fqX
37351 -----END CERTIFICATE-----
37352
37353 GlobalSign Root CA - R3
37354 =======================
37355 -----BEGIN CERTIFICATE-----
37356 MIIDXzCCAkegAwIBAgILBAAAAAABIVhTCKIwDQYJKoZIhvcNAQELBQAwTDEgMB4GA1UECxMXR2xv
37357 YmFsU2lnbiBSb290IENBIC0gUjMxEzARBgNVBAoTCkdsb2JhbFNpZ24xEzARBgNVBAMTCkdsb2Jh
37358 bFNpZ24wHhcNMDkwMzE4MTAwMDAwWhcNMjkwMzE4MTAwMDAwWjBMMSAwHgYDVQQLExdHbG9iYWxT
37359 aWduIFJvb3QgQ0EgLSBSMzETMBEGA1UEChMKR2xvYmFsU2lnbjETMBEGA1UEAxMKR2xvYmFsU2ln
37360 bjCCASIwDQYJKoZIhvcNAQEBBQADggEPADCCAQoCggEBAMwldpB5BngiFvXAg7aEyiie/QV2EcWt
37361 iHL8RgJDx7KKnQRfJMsuS+FggkbhUqsMgUdwbN1k0ev1LKMPgj0MK66X17YUhhB5uzsTgHeMCOFJ
37362 0mpiLx9e+pZo34knlTifBtc+ycsmWQ1z3rDI6SYOgxXG71uL0gRgykmmKPZpO/bLyCiR5Z2KYVc3
37363 rHQU3HTgOu5yLy6c+9C7v/U9AOEGM+iCK65TpjoWc4zdQQ4gOsC0p6Hpsk+QLjJg6VfLuQSSaGjl
37364 OCZgdbKfd/+RFO+uIEn8rUAVSNECMWEZXriX7613t2Saer9fwRPvm2L7DWzgVGkWqQPabumDk3F2
37365 xmmFghcCAwEAAaNCMEAwDgYDVR0PAQH/BAQDAgEGMA8GA1UdEwEB/wQFMAMBAf8wHQYDVR0OBBYE
37366 FI/wS3+oLkUkrk1Q+mOai97i3Ru8MA0GCSqGSIb3DQEBCwUAA4IBAQBLQNvAUKr+yAzv95ZURUm7
37367 lgAJQayzE4aGKAczymvmdLm6AC2upArT9fHxD4q/c2dKg8dEe3jgr25sbwMpjjM5RcOO5LlXbKr8
37368 EpbsU8Yt5CRsuZRj+9xTaGdWPoO4zzUhw8lo/s7awlOqzJCK6fBdRoyV3XpYKBovHd7NADdBj+1E
37369 bddTKJd+82cEHhXXipa0095MJ6RMG3NzdvQXmcIfeg7jLQitChws/zyrVQ4PkX4268NXSb7hLi18
37370 YIvDQVETI53O9zJrlAGomecsMx86OyXShkDOOyyGeMlhLxS67ttVb9+E7gUJTb0o2HLO02JQZR7r
37371 kpeDMdmztcpHWD9f
37372 -----END CERTIFICATE-----
37373
37374 TC TrustCenter Universal CA III
37375 ===============================
37376 -----BEGIN CERTIFICATE-----
37377 MIID4TCCAsmgAwIBAgIOYyUAAQACFI0zFQLkbPQwDQYJKoZIhvcNAQEFBQAwezELMAkGA1UEBhMC
37378 REUxHDAaBgNVBAoTE1RDIFRydXN0Q2VudGVyIEdtYkgxJDAiBgNVBAsTG1RDIFRydXN0Q2VudGVy
37379 IFVuaXZlcnNhbCBDQTEoMCYGA1UEAxMfVEMgVHJ1c3RDZW50ZXIgVW5pdmVyc2FsIENBIElJSTAe
37380 Fw0wOTA5MDkwODE1MjdaFw0yOTEyMzEyMzU5NTlaMHsxCzAJBgNVBAYTAkRFMRwwGgYDVQQKExNU
37381 QyBUcnVzdENlbnRlciBHbWJIMSQwIgYDVQQLExtUQyBUcnVzdENlbnRlciBVbml2ZXJzYWwgQ0Ex
37382 KDAmBgNVBAMTH1RDIFRydXN0Q2VudGVyIFVuaXZlcnNhbCBDQSBJSUkwggEiMA0GCSqGSIb3DQEB
37383 AQUAA4IBDwAwggEKAoIBAQDC2pxisLlxErALyBpXsq6DFJmzNEubkKLF5+cvAqBNLaT6hdqbJYUt
37384 QCggbergvbFIgyIpRJ9Og+41URNzdNW88jBmlFPAQDYvDIRlzg9uwliT6CwLOunBjvvya8o84pxO
37385 juT5fdMnnxvVZ3iHLX8LR7PH6MlIfK8vzArZQe+f/prhsq75U7Xl6UafYOPfjdN/+5Z+s7Vy+Eut
37386 CHnNaYlAJ/Uqwa1D7KRTyGG299J5KmcYdkhtWyUB0SbFt1dpIxVbYYqt8Bst2a9c8SaQaanVDED1
37387 M4BDj5yjdipFtK+/fz6HP3bFzSreIMUWWMv5G/UPyw0RUmS40nZid4PxWJ//AgMBAAGjYzBhMB8G
37388 A1UdIwQYMBaAFFbn4VslQ4Dg9ozhcbyO5YAvxEjiMA8GA1UdEwEB/wQFMAMBAf8wDgYDVR0PAQH/
37389 BAQDAgEGMB0GA1UdDgQWBBRW5+FbJUOA4PaM4XG8juWAL8RI4jANBgkqhkiG9w0BAQUFAAOCAQEA
37390 g8ev6n9NCjw5sWi+e22JLumzCecYV42FmhfzdkJQEw/HkG8zrcVJYCtsSVgZ1OK+t7+rSbyUyKu+
37391 KGwWaODIl0YgoGhnYIg5IFHYaAERzqf2EQf27OysGh+yZm5WZ2B6dF7AbZc2rrUNXWZzwCUyRdhK
37392 BgePxLcHsU0GDeGl6/R1yrqc0L2z0zIkTO5+4nYES0lT2PLpVDP85XEfPRRclkvxOvIAu2y0+pZV
37393 CIgJwcyRGSmwIC3/yzikQOEXvnlhgP8HA4ZMTnsGnxGGjYnuJ8Tb4rwZjgvDwxPHLQNjO9Po5KIq
37394 woIIlBZU8O8fJ5AluA0OKBtHd0e9HKgl8ZS0Zg==
37395 -----END CERTIFICATE-----
37396
37397 Autoridad de Certificacion Firmaprofesional CIF A62634068
37398 =========================================================
37399 -----BEGIN CERTIFICATE-----
37400 MIIGFDCCA/ygAwIBAgIIU+w77vuySF8wDQYJKoZIhvcNAQEFBQAwUTELMAkGA1UEBhMCRVMxQjBA
37401 BgNVBAMMOUF1dG9yaWRhZCBkZSBDZXJ0aWZpY2FjaW9uIEZpcm1hcHJvZmVzaW9uYWwgQ0lGIEE2
37402 MjYzNDA2ODAeFw0wOTA1MjAwODM4MTVaFw0zMDEyMzEwODM4MTVaMFExCzAJBgNVBAYTAkVTMUIw
37403 QAYDVQQDDDlBdXRvcmlkYWQgZGUgQ2VydGlmaWNhY2lvbiBGaXJtYXByb2Zlc2lvbmFsIENJRiBB
37404 NjI2MzQwNjgwggIiMA0GCSqGSIb3DQEBAQUAA4ICDwAwggIKAoICAQDKlmuO6vj78aI14H9M2uDD
37405 Utd9thDIAl6zQyrET2qyyhxdKJp4ERppWVevtSBC5IsP5t9bpgOSL/UR5GLXMnE42QQMcas9UX4P
37406 B99jBVzpv5RvwSmCwLTaUbDBPLutN0pcyvFLNg4kq7/DhHf9qFD0sefGL9ItWY16Ck6WaVICqjaY
37407 7Pz6FIMMNx/Jkjd/14Et5cS54D40/mf0PmbR0/RAz15iNA9wBj4gGFrO93IbJWyTdBSTo3OxDqqH
37408 ECNZXyAFGUftaI6SEspd/NYrspI8IM/hX68gvqB2f3bl7BqGYTM+53u0P6APjqK5am+5hyZvQWyI
37409 plD9amML9ZMWGxmPsu2bm8mQ9QEM3xk9Dz44I8kvjwzRAv4bVdZO0I08r0+k8/6vKtMFnXkIoctX
37410 MbScyJCyZ/QYFpM6/EfY0XiWMR+6KwxfXZmtY4laJCB22N/9q06mIqqdXuYnin1oKaPnirjaEbsX
37411 LZmdEyRG98Xi2J+Of8ePdG1asuhy9azuJBCtLxTa/y2aRnFHvkLfuwHb9H/TKI8xWVvTyQKmtFLK
37412 bpf7Q8UIJm+K9Lv9nyiqDdVF8xM6HdjAeI9BZzwelGSuewvF6NkBiDkal4ZkQdU7hwxu+g/GvUgU
37413 vzlN1J5Bto+WHWOWk9mVBngxaJ43BjuAiUVhOSPHG0SjFeUc+JIwuwIDAQABo4HvMIHsMBIGA1Ud
37414 EwEB/wQIMAYBAf8CAQEwDgYDVR0PAQH/BAQDAgEGMB0GA1UdDgQWBBRlzeurNR4APn7VdMActHNH
37415 DhpkLzCBpgYDVR0gBIGeMIGbMIGYBgRVHSAAMIGPMC8GCCsGAQUFBwIBFiNodHRwOi8vd3d3LmZp
37416 cm1hcHJvZmVzaW9uYWwuY29tL2NwczBcBggrBgEFBQcCAjBQHk4AUABhAHMAZQBvACAAZABlACAA
37417 bABhACAAQgBvAG4AYQBuAG8AdgBhACAANAA3ACAAQgBhAHIAYwBlAGwAbwBuAGEAIAAwADgAMAAx
37418 ADcwDQYJKoZIhvcNAQEFBQADggIBABd9oPm03cXF661LJLWhAqvdpYhKsg9VSytXjDvlMd3+xDLx
37419 51tkljYyGOylMnfX40S2wBEqgLk9am58m9Ot/MPWo+ZkKXzR4Tgegiv/J2Wv+xYVxC5xhOW1//qk
37420 R71kMrv2JYSiJ0L1ILDCExARzRAVukKQKtJE4ZYm6zFIEv0q2skGz3QeqUvVhyj5eTSSPi5E6PaP
37421 T481PyWzOdxjKpBrIF/EUhJOlywqrJ2X3kjyo2bbwtKDlaZmp54lD+kLM5FlClrD2VQS3a/DTg4f
37422 Jl4N3LON7NWBcN7STyQF82xO9UxJZo3R/9ILJUFI/lGExkKvgATP0H5kSeTy36LssUzAKh3ntLFl
37423 osS88Zj0qnAHY7S42jtM+kAiMFsRpvAFDsYCA0irhpuF3dvd6qJ2gHN99ZwExEWN57kci57q13XR
37424 crHedUTnQn3iV2t93Jm8PYMo6oCTjcVMZcFwgbg4/EMxsvYDNEeyrPsiBsse3RdHHF9mudMaotoR
37425 saS8I8nkvof/uZS2+F0gStRf571oe2XyFR7SOqkt6dhrJKyXWERHrVkY8SFlcN7ONGCoQPHzPKTD
37426 KCOM/iczQ0CgFzzr6juwcqajuUpLXhZI9LK8yIySxZ2frHI2vDSANGupi5LAuBft7HZT9SQBjLMi
37427 6Et8Vcad+qMUu2WFbm5PEn4KPJ2V
37428 -----END CERTIFICATE-----
37429
37430 Izenpe.com
37431 ==========
37432 -----BEGIN CERTIFICATE-----
37433 MIIF8TCCA9mgAwIBAgIQALC3WhZIX7/hy/WL1xnmfTANBgkqhkiG9w0BAQsFADA4MQswCQYDVQQG
37434 EwJFUzEUMBIGA1UECgwLSVpFTlBFIFMuQS4xEzARBgNVBAMMCkl6ZW5wZS5jb20wHhcNMDcxMjEz
37435 MTMwODI4WhcNMzcxMjEzMDgyNzI1WjA4MQswCQYDVQQGEwJFUzEUMBIGA1UECgwLSVpFTlBFIFMu
37436 QS4xEzARBgNVBAMMCkl6ZW5wZS5jb20wggIiMA0GCSqGSIb3DQEBAQUAA4ICDwAwggIKAoICAQDJ
37437 03rKDx6sp4boFmVqscIbRTJxldn+EFvMr+eleQGPicPK8lVx93e+d5TzcqQsRNiekpsUOqHnJJAK
37438 ClaOxdgmlOHZSOEtPtoKct2jmRXagaKH9HtuJneJWK3W6wyyQXpzbm3benhB6QiIEn6HLmYRY2xU
37439 +zydcsC8Lv/Ct90NduM61/e0aL6i9eOBbsFGb12N4E3GVFWJGjMxCrFXuaOKmMPsOzTFlUFpfnXC
37440 PCDFYbpRR6AgkJOhkEvzTnyFRVSa0QUmQbC1TR0zvsQDyCV8wXDbO/QJLVQnSKwv4cSsPsjLkkxT
37441 OTcj7NMB+eAJRE1NZMDhDVqHIrytG6P+JrUV86f8hBnp7KGItERphIPzidF0BqnMC9bC3ieFUCbK
37442 F7jJeodWLBoBHmy+E60QrLUk9TiRodZL2vG70t5HtfG8gfZZa88ZU+mNFctKy6lvROUbQc/hhqfK
37443 0GqfvEyNBjNaooXlkDWgYlwWTvDjovoDGrQscbNYLN57C9saD+veIR8GdwYDsMnvmfzAuU8Lhij+
37444 0rnq49qlw0dpEuDb8PYZi+17cNcC1u2HGCgsBCRMd+RIihrGO5rUD8r6ddIBQFqNeb+Lz0vPqhbB
37445 leStTIo+F5HUsWLlguWABKQDfo2/2n+iD5dPDNMN+9fR5XJ+HMh3/1uaD7euBUbl8agW7EekFwID
37446 AQABo4H2MIHzMIGwBgNVHREEgagwgaWBD2luZm9AaXplbnBlLmNvbaSBkTCBjjFHMEUGA1UECgw+
37447 SVpFTlBFIFMuQS4gLSBDSUYgQTAxMzM3MjYwLVJNZXJjLlZpdG9yaWEtR2FzdGVpeiBUMTA1NSBG
37448 NjIgUzgxQzBBBgNVBAkMOkF2ZGEgZGVsIE1lZGl0ZXJyYW5lbyBFdG9yYmlkZWEgMTQgLSAwMTAx
37449 MCBWaXRvcmlhLUdhc3RlaXowDwYDVR0TAQH/BAUwAwEB/zAOBgNVHQ8BAf8EBAMCAQYwHQYDVR0O
37450 BBYEFB0cZQ6o8iV7tJHP5LGx5r1VdGwFMA0GCSqGSIb3DQEBCwUAA4ICAQB4pgwWSp9MiDrAyw6l
37451 Fn2fuUhfGI8NYjb2zRlrrKvV9pF9rnHzP7MOeIWblaQnIUdCSnxIOvVFfLMMjlF4rJUT3sb9fbga
37452 kEyrkgPH7UIBzg/YsfqikuFgba56awmqxinuaElnMIAkejEWOVt+8Rwu3WwJrfIxwYJOubv5vr8q
37453 hT/AQKM6WfxZSzwoJNu0FXWuDYi6LnPAvViH5ULy617uHjAimcs30cQhbIHsvm0m5hzkQiCeR7Cs
37454 g1lwLDXWrzY0tM07+DKo7+N4ifuNRSzanLh+QBxh5z6ikixL8s36mLYp//Pye6kfLqCTVyvehQP5
37455 aTfLnnhqBbTFMXiJ7HqnheG5ezzevh55hM6fcA5ZwjUukCox2eRFekGkLhObNA5me0mrZJfQRsN5
37456 nXJQY6aYWwa9SG3YOYNw6DXwBdGqvOPbyALqfP2C2sJbUjWumDqtujWTI6cfSN01RpiyEGjkpTHC
37457 ClguGYEQyVB1/OpaFs4R1+7vUIgtYf8/QnMFlEPVjjxOAToZpR9GTnfQXeWBIiGH/pR9hNiTrdZo
37458 Q0iy2+tzJOeRf1SktoA+naM8THLCV8Sg1Mw4J87VBp6iSNnpn86CcDaTmjvfliHjWbcM2pE38P1Z
37459 WrOZyGlsQyYBNWNgVYkDOnXYukrZVP/u3oDYLdE41V4tC5h9Pmzb/CaIxw==
37460 -----END CERTIFICATE-----
37461
37462 Chambers of Commerce Root - 2008
37463 ================================
37464 -----BEGIN CERTIFICATE-----
37465 MIIHTzCCBTegAwIBAgIJAKPaQn6ksa7aMA0GCSqGSIb3DQEBBQUAMIGuMQswCQYDVQQGEwJFVTFD
37466 MEEGA1UEBxM6TWFkcmlkIChzZWUgY3VycmVudCBhZGRyZXNzIGF0IHd3dy5jYW1lcmZpcm1hLmNv
37467 bS9hZGRyZXNzKTESMBAGA1UEBRMJQTgyNzQzMjg3MRswGQYDVQQKExJBQyBDYW1lcmZpcm1hIFMu
37468 QS4xKTAnBgNVBAMTIENoYW1iZXJzIG9mIENvbW1lcmNlIFJvb3QgLSAyMDA4MB4XDTA4MDgwMTEy
37469 Mjk1MFoXDTM4MDczMTEyMjk1MFowga4xCzAJBgNVBAYTAkVVMUMwQQYDVQQHEzpNYWRyaWQgKHNl
37470 ZSBjdXJyZW50IGFkZHJlc3MgYXQgd3d3LmNhbWVyZmlybWEuY29tL2FkZHJlc3MpMRIwEAYDVQQF
37471 EwlBODI3NDMyODcxGzAZBgNVBAoTEkFDIENhbWVyZmlybWEgUy5BLjEpMCcGA1UEAxMgQ2hhbWJl
37472 cnMgb2YgQ29tbWVyY2UgUm9vdCAtIDIwMDgwggIiMA0GCSqGSIb3DQEBAQUAA4ICDwAwggIKAoIC
37473 AQCvAMtwNyuAWko6bHiUfaN/Gh/2NdW928sNRHI+JrKQUrpjOyhYb6WzbZSm891kDFX29ufyIiKA
37474 XuFixrYp4YFs8r/lfTJqVKAyGVn+H4vXPWCGhSRv4xGzdz4gljUha7MI2XAuZPeEklPWDrCQiorj
37475 h40G072QDuKZoRuGDtqaCrsLYVAGUvGef3bsyw/QHg3PmTA9HMRFEFis1tPo1+XqxQEHd9ZR5gN/
37476 ikilTWh1uem8nk4ZcfUyS5xtYBkL+8ydddy/Js2Pk3g5eXNeJQ7KXOt3EgfLZEFHcpOrUMPrCXZk
37477 NNI5t3YRCQ12RcSprj1qr7V9ZS+UWBDsXHyvfuK2GNnQm05aSd+pZgvMPMZ4fKecHePOjlO+Bd5g
37478 D2vlGts/4+EhySnB8esHnFIbAURRPHsl18TlUlRdJQfKFiC4reRB7noI/plvg6aRArBsNlVq5331
37479 lubKgdaX8ZSD6e2wsWsSaR6s+12pxZjptFtYer49okQ6Y1nUCyXeG0+95QGezdIp1Z8XGQpvvwyQ
37480 0wlf2eOKNcx5Wk0ZN5K3xMGtr/R5JJqyAQuxr1yW84Ay+1w9mPGgP0revq+ULtlVmhduYJ1jbLhj
37481 ya6BXBg14JC7vjxPNyK5fuvPnnchpj04gftI2jE9K+OJ9dC1vX7gUMQSibMjmhAxhduub+84Mxh2
37482 EQIDAQABo4IBbDCCAWgwEgYDVR0TAQH/BAgwBgEB/wIBDDAdBgNVHQ4EFgQU+SSsD7K1+HnA+mCI
37483 G8TZTQKeFxkwgeMGA1UdIwSB2zCB2IAU+SSsD7K1+HnA+mCIG8TZTQKeFxmhgbSkgbEwga4xCzAJ
37484 BgNVBAYTAkVVMUMwQQYDVQQHEzpNYWRyaWQgKHNlZSBjdXJyZW50IGFkZHJlc3MgYXQgd3d3LmNh
37485 bWVyZmlybWEuY29tL2FkZHJlc3MpMRIwEAYDVQQFEwlBODI3NDMyODcxGzAZBgNVBAoTEkFDIENh
37486 bWVyZmlybWEgUy5BLjEpMCcGA1UEAxMgQ2hhbWJlcnMgb2YgQ29tbWVyY2UgUm9vdCAtIDIwMDiC
37487 CQCj2kJ+pLGu2jAOBgNVHQ8BAf8EBAMCAQYwPQYDVR0gBDYwNDAyBgRVHSAAMCowKAYIKwYBBQUH
37488 AgEWHGh0dHA6Ly9wb2xpY3kuY2FtZXJmaXJtYS5jb20wDQYJKoZIhvcNAQEFBQADggIBAJASryI1
37489 wqM58C7e6bXpeHxIvj99RZJe6dqxGfwWPJ+0W2aeaufDuV2I6A+tzyMP3iU6XsxPpcG1Lawk0lgH
37490 3qLPaYRgM+gQDROpI9CF5Y57pp49chNyM/WqfcZjHwj0/gF/JM8rLFQJ3uIrbZLGOU8W6jx+ekbU
37491 RWpGqOt1glanq6B8aBMz9p0w8G8nOSQjKpD9kCk18pPfNKXG9/jvjA9iSnyu0/VU+I22mlaHFoI6
37492 M6taIgj3grrqLuBHmrS1RaMFO9ncLkVAO+rcf+g769HsJtg1pDDFOqxXnrN2pSB7+R5KBWIBpih1
37493 YJeSDW4+TTdDDZIVnBgizVGZoCkaPF+KMjNbMMeJL0eYD6MDxvbxrN8y8NmBGuScvfaAFPDRLLmF
37494 9dijscilIeUcE5fuDr3fKanvNFNb0+RqE4QGtjICxFKuItLcsiFCGtpA8CnJ7AoMXOLQusxI0zcK
37495 zBIKinmwPQN/aUv0NCB9szTqjktk9T79syNnFQ0EuPAtwQlRPLJsFfClI9eDdOTlLsn+mCdCxqvG
37496 nrDQWzilm1DefhiYtUU79nm06PcaewaD+9CL2rvHvRirCG88gGtAPxkZumWK5r7VXNM21+9AUiRg
37497 OGcEMeyP84LG3rlV8zsxkVrctQgVrXYlCg17LofiDKYGvCYQbTed7N14jHyAxfDZd0jQ
37498 -----END CERTIFICATE-----
37499
37500 Global Chambersign Root - 2008
37501 ==============================
37502 -----BEGIN CERTIFICATE-----
37503 MIIHSTCCBTGgAwIBAgIJAMnN0+nVfSPOMA0GCSqGSIb3DQEBBQUAMIGsMQswCQYDVQQGEwJFVTFD
37504 MEEGA1UEBxM6TWFkcmlkIChzZWUgY3VycmVudCBhZGRyZXNzIGF0IHd3dy5jYW1lcmZpcm1hLmNv
37505 bS9hZGRyZXNzKTESMBAGA1UEBRMJQTgyNzQzMjg3MRswGQYDVQQKExJBQyBDYW1lcmZpcm1hIFMu
37506 QS4xJzAlBgNVBAMTHkdsb2JhbCBDaGFtYmVyc2lnbiBSb290IC0gMjAwODAeFw0wODA4MDExMjMx
37507 NDBaFw0zODA3MzExMjMxNDBaMIGsMQswCQYDVQQGEwJFVTFDMEEGA1UEBxM6TWFkcmlkIChzZWUg
37508 Y3VycmVudCBhZGRyZXNzIGF0IHd3dy5jYW1lcmZpcm1hLmNvbS9hZGRyZXNzKTESMBAGA1UEBRMJ
37509 QTgyNzQzMjg3MRswGQYDVQQKExJBQyBDYW1lcmZpcm1hIFMuQS4xJzAlBgNVBAMTHkdsb2JhbCBD
37510 aGFtYmVyc2lnbiBSb290IC0gMjAwODCCAiIwDQYJKoZIhvcNAQEBBQADggIPADCCAgoCggIBAMDf
37511 VtPkOpt2RbQT2//BthmLN0EYlVJH6xedKYiONWwGMi5HYvNJBL99RDaxccy9Wglz1dmFRP+RVyXf
37512 XjaOcNFccUMd2drvXNL7G706tcuto8xEpw2uIRU/uXpbknXYpBI4iRmKt4DS4jJvVpyR1ogQC7N0
37513 ZJJ0YPP2zxhPYLIj0Mc7zmFLmY/CDNBAspjcDahOo7kKrmCgrUVSY7pmvWjg+b4aqIG7HkF4ddPB
37514 /gBVsIdU6CeQNR1MM62X/JcumIS/LMmjv9GYERTtY/jKmIhYF5ntRQOXfjyGHoiMvvKRhI9lNNgA
37515 TH23MRdaKXoKGCQwoze1eqkBfSbW+Q6OWfH9GzO1KTsXO0G2Id3UwD2ln58fQ1DJu7xsepeY7s2M
37516 H/ucUa6LcL0nn3HAa6x9kGbo1106DbDVwo3VyJ2dwW3Q0L9R5OP4wzg2rtandeavhENdk5IMagfe
37517 Ox2YItaswTXbo6Al/3K1dh3ebeksZixShNBFks4c5eUzHdwHU1SjqoI7mjcv3N2gZOnm3b2u/GSF
37518 HTynyQbehP9r6GsaPMWis0L7iwk+XwhSx2LE1AVxv8Rk5Pihg+g+EpuoHtQ2TS9x9o0o9oOpE9Jh
37519 wZG7SMA0j0GMS0zbaRL/UJScIINZc+18ofLx/d33SdNDWKBWY8o9PeU1VlnpDsogzCtLkykPAgMB
37520 AAGjggFqMIIBZjASBgNVHRMBAf8ECDAGAQH/AgEMMB0GA1UdDgQWBBS5CcqcHtvTbDprru1U8VuT
37521 BjUuXjCB4QYDVR0jBIHZMIHWgBS5CcqcHtvTbDprru1U8VuTBjUuXqGBsqSBrzCBrDELMAkGA1UE
37522 BhMCRVUxQzBBBgNVBAcTOk1hZHJpZCAoc2VlIGN1cnJlbnQgYWRkcmVzcyBhdCB3d3cuY2FtZXJm
37523 aXJtYS5jb20vYWRkcmVzcykxEjAQBgNVBAUTCUE4Mjc0MzI4NzEbMBkGA1UEChMSQUMgQ2FtZXJm
37524 aXJtYSBTLkEuMScwJQYDVQQDEx5HbG9iYWwgQ2hhbWJlcnNpZ24gUm9vdCAtIDIwMDiCCQDJzdPp
37525 1X0jzjAOBgNVHQ8BAf8EBAMCAQYwPQYDVR0gBDYwNDAyBgRVHSAAMCowKAYIKwYBBQUHAgEWHGh0
37526 dHA6Ly9wb2xpY3kuY2FtZXJmaXJtYS5jb20wDQYJKoZIhvcNAQEFBQADggIBAICIf3DekijZBZRG
37527 /5BXqfEv3xoNa/p8DhxJJHkn2EaqbylZUohwEurdPfWbU1Rv4WCiqAm57OtZfMY18dwY6fFn5a+6
37528 ReAJ3spED8IXDneRRXozX1+WLGiLwUePmJs9wOzL9dWCkoQ10b42OFZyMVtHLaoXpGNR6woBrX/s
37529 dZ7LoR/xfxKxueRkf2fWIyr0uDldmOghp+G9PUIadJpwr2hsUF1Jz//7Dl3mLEfXgTpZALVza2Mg
37530 9jFFCDkO9HB+QHBaP9BrQql0PSgvAm11cpUJjUhjxsYjV5KTXjXBjfkK9yydYhz2rXzdpjEetrHH
37531 foUm+qRqtdpjMNHvkzeyZi99Bffnt0uYlDXA2TopwZ2yUDMdSqlapskD7+3056huirRXhOukP9Du
37532 qqqHW2Pok+JrqNS4cnhrG+055F3Lm6qH1U9OAP7Zap88MQ8oAgF9mOinsKJknnn4SPIVqczmyETr
37533 P3iZ8ntxPjzxmKfFGBI/5rsoM0LpRQp8bfKGeS/Fghl9CYl8slR2iK7ewfPM4W7bMdaTrpmg7yVq
37534 c5iJWzouE4gev8CSlDQb4ye3ix5vQv/n6TebUB0tovkC7stYWDpxvGjjqsGvHCgfotwjZT+B6q6Z
37535 09gwzxMNTxXJhLynSC34MCN32EZLeW32jO06f2ARePTpm67VVMB0gNELQp/B
37536 -----END CERTIFICATE-----
37537
37538 Go Daddy Root Certificate Authority - G2
37539 ========================================
37540 -----BEGIN CERTIFICATE-----
37541 MIIDxTCCAq2gAwIBAgIBADANBgkqhkiG9w0BAQsFADCBgzELMAkGA1UEBhMCVVMxEDAOBgNVBAgT
37542 B0FyaXpvbmExEzARBgNVBAcTClNjb3R0c2RhbGUxGjAYBgNVBAoTEUdvRGFkZHkuY29tLCBJbmMu
37543 MTEwLwYDVQQDEyhHbyBEYWRkeSBSb290IENlcnRpZmljYXRlIEF1dGhvcml0eSAtIEcyMB4XDTA5
37544 MDkwMTAwMDAwMFoXDTM3MTIzMTIzNTk1OVowgYMxCzAJBgNVBAYTAlVTMRAwDgYDVQQIEwdBcml6
37545 b25hMRMwEQYDVQQHEwpTY290dHNkYWxlMRowGAYDVQQKExFHb0RhZGR5LmNvbSwgSW5jLjExMC8G
37546 A1UEAxMoR28gRGFkZHkgUm9vdCBDZXJ0aWZpY2F0ZSBBdXRob3JpdHkgLSBHMjCCASIwDQYJKoZI
37547 hvcNAQEBBQADggEPADCCAQoCggEBAL9xYgjx+lk09xvJGKP3gElY6SKDE6bFIEMBO4Tx5oVJnyfq
37548 9oQbTqC023CYxzIBsQU+B07u9PpPL1kwIuerGVZr4oAH/PMWdYA5UXvl+TW2dE6pjYIT5LY/qQOD
37549 +qK+ihVqf94Lw7YZFAXK6sOoBJQ7RnwyDfMAZiLIjWltNowRGLfTshxgtDj6AozO091GB94KPutd
37550 fMh8+7ArU6SSYmlRJQVhGkSBjCypQ5Yj36w6gZoOKcUcqeldHraenjAKOc7xiID7S13MMuyFYkMl
37551 NAJWJwGRtDtwKj9useiciAF9n9T521NtYJ2/LOdYq7hfRvzOxBsDPAnrSTFcaUaz4EcCAwEAAaNC
37552 MEAwDwYDVR0TAQH/BAUwAwEB/zAOBgNVHQ8BAf8EBAMCAQYwHQYDVR0OBBYEFDqahQcQZyi27/a9
37553 BUFuIMGU2g/eMA0GCSqGSIb3DQEBCwUAA4IBAQCZ21151fmXWWcDYfF+OwYxdS2hII5PZYe096ac
37554 vNjpL9DbWu7PdIxztDhC2gV7+AJ1uP2lsdeu9tfeE8tTEH6KRtGX+rcuKxGrkLAngPnon1rpN5+r
37555 5N9ss4UXnT3ZJE95kTXWXwTrgIOrmgIttRD02JDHBHNA7XIloKmf7J6raBKZV8aPEjoJpL1E/QYV
37556 N8Gb5DKj7Tjo2GTzLH4U/ALqn83/B2gX2yKQOC16jdFU8WnjXzPKej17CuPKf1855eJ1usV2GDPO
37557 LPAvTK33sefOT6jEm0pUBsV/fdUID+Ic/n4XuKxe9tQWskMJDE32p2u0mYRlynqI4uJEvlz36hz1
37558 -----END CERTIFICATE-----
37559
37560 Starfield Root Certificate Authority - G2
37561 =========================================
37562 -----BEGIN CERTIFICATE-----
37563 MIID3TCCAsWgAwIBAgIBADANBgkqhkiG9w0BAQsFADCBjzELMAkGA1UEBhMCVVMxEDAOBgNVBAgT
37564 B0FyaXpvbmExEzARBgNVBAcTClNjb3R0c2RhbGUxJTAjBgNVBAoTHFN0YXJmaWVsZCBUZWNobm9s
37565 b2dpZXMsIEluYy4xMjAwBgNVBAMTKVN0YXJmaWVsZCBSb290IENlcnRpZmljYXRlIEF1dGhvcml0
37566 eSAtIEcyMB4XDTA5MDkwMTAwMDAwMFoXDTM3MTIzMTIzNTk1OVowgY8xCzAJBgNVBAYTAlVTMRAw
37567 DgYDVQQIEwdBcml6b25hMRMwEQYDVQQHEwpTY290dHNkYWxlMSUwIwYDVQQKExxTdGFyZmllbGQg
37568 VGVjaG5vbG9naWVzLCBJbmMuMTIwMAYDVQQDEylTdGFyZmllbGQgUm9vdCBDZXJ0aWZpY2F0ZSBB
37569 dXRob3JpdHkgLSBHMjCCASIwDQYJKoZIhvcNAQEBBQADggEPADCCAQoCggEBAL3twQP89o/8ArFv
37570 W59I2Z154qK3A2FWGMNHttfKPTUuiUP3oWmb3ooa/RMgnLRJdzIpVv257IzdIvpy3Cdhl+72WoTs
37571 bhm5iSzchFvVdPtrX8WJpRBSiUZV9Lh1HOZ/5FSuS/hVclcCGfgXcVnrHigHdMWdSL5stPSksPNk
37572 N3mSwOxGXn/hbVNMYq/NHwtjuzqd+/x5AJhhdM8mgkBj87JyahkNmcrUDnXMN/uLicFZ8WJ/X7Nf
37573 ZTD4p7dNdloedl40wOiWVpmKs/B/pM293DIxfJHP4F8R+GuqSVzRmZTRouNjWwl2tVZi4Ut0HZbU
37574 JtQIBFnQmA4O5t78w+wfkPECAwEAAaNCMEAwDwYDVR0TAQH/BAUwAwEB/zAOBgNVHQ8BAf8EBAMC
37575 AQYwHQYDVR0OBBYEFHwMMh+n2TB/xH1oo2Kooc6rB1snMA0GCSqGSIb3DQEBCwUAA4IBAQARWfol
37576 TwNvlJk7mh+ChTnUdgWUXuEok21iXQnCoKjUsHU48TRqneSfioYmUeYs0cYtbpUgSpIB7LiKZ3sx
37577 4mcujJUDJi5DnUox9g61DLu34jd/IroAow57UvtruzvE03lRTs2Q9GcHGcg8RnoNAX3FWOdt5oUw
37578 F5okxBDgBPfg8n/Uqgr/Qh037ZTlZFkSIHc40zI+OIF1lnP6aI+xy84fxez6nH7PfrHxBy22/L/K
37579 pL/QlwVKvOoYKAKQvVR4CSFx09F9HdkWsKlhPdAKACL8x3vLCWRFCztAgfd9fDL1mMpYjn0q7pBZ
37580 c2T5NnReJaH1ZgUufzkVqSr7UIuOhWn0
37581 -----END CERTIFICATE-----
37582
37583 Starfield Services Root Certificate Authority - G2
37584 ==================================================
37585 -----BEGIN CERTIFICATE-----
37586 MIID7zCCAtegAwIBAgIBADANBgkqhkiG9w0BAQsFADCBmDELMAkGA1UEBhMCVVMxEDAOBgNVBAgT
37587 B0FyaXpvbmExEzARBgNVBAcTClNjb3R0c2RhbGUxJTAjBgNVBAoTHFN0YXJmaWVsZCBUZWNobm9s
37588 b2dpZXMsIEluYy4xOzA5BgNVBAMTMlN0YXJmaWVsZCBTZXJ2aWNlcyBSb290IENlcnRpZmljYXRl
37589 IEF1dGhvcml0eSAtIEcyMB4XDTA5MDkwMTAwMDAwMFoXDTM3MTIzMTIzNTk1OVowgZgxCzAJBgNV
37590 BAYTAlVTMRAwDgYDVQQIEwdBcml6b25hMRMwEQYDVQQHEwpTY290dHNkYWxlMSUwIwYDVQQKExxT
37591 dGFyZmllbGQgVGVjaG5vbG9naWVzLCBJbmMuMTswOQYDVQQDEzJTdGFyZmllbGQgU2VydmljZXMg
37592 Um9vdCBDZXJ0aWZpY2F0ZSBBdXRob3JpdHkgLSBHMjCCASIwDQYJKoZIhvcNAQEBBQADggEPADCC
37593 AQoCggEBANUMOsQq+U7i9b4Zl1+OiFOxHz/Lz58gE20pOsgPfTz3a3Y4Y9k2YKibXlwAgLIvWX/2
37594 h/klQ4bnaRtSmpDhcePYLQ1Ob/bISdm28xpWriu2dBTrz/sm4xq6HZYuajtYlIlHVv8loJNwU4Pa
37595 hHQUw2eeBGg6345AWh1KTs9DkTvnVtYAcMtS7nt9rjrnvDH5RfbCYM8TWQIrgMw0R9+53pBlbQLP
37596 LJGmpufehRhJfGZOozptqbXuNC66DQO4M99H67FrjSXZm86B0UVGMpZwh94CDklDhbZsc7tk6mFB
37597 rMnUVN+HL8cisibMn1lUaJ/8viovxFUcdUBgF4UCVTmLfwUCAwEAAaNCMEAwDwYDVR0TAQH/BAUw
37598 AwEB/zAOBgNVHQ8BAf8EBAMCAQYwHQYDVR0OBBYEFJxfAN+qAdcwKziIorhtSpzyEZGDMA0GCSqG
37599 SIb3DQEBCwUAA4IBAQBLNqaEd2ndOxmfZyMIbw5hyf2E3F/YNoHN2BtBLZ9g3ccaaNnRbobhiCPP
37600 E95Dz+I0swSdHynVv/heyNXBve6SbzJ08pGCL72CQnqtKrcgfU28elUSwhXqvfdqlS5sdJ/PHLTy
37601 xQGjhdByPq1zqwubdQxtRbeOlKyWN7Wg0I8VRw7j6IPdj/3vQQF3zCepYoUz8jcI73HPdwbeyBkd
37602 iEDPfUYd/x7H4c7/I9vG+o1VTqkC50cRRj70/b17KSa7qWFiNyi2LSr2EIZkyXCn0q23KXB56jza
37603 YyWf/Wi3MOxw+3WKt21gZ7IeyLnp2KhvAotnDU0mV3HaIPzBSlCNsSi6
37604 -----END CERTIFICATE-----
37605
37606 AffirmTrust Commercial
37607 ======================
37608 -----BEGIN CERTIFICATE-----
37609 MIIDTDCCAjSgAwIBAgIId3cGJyapsXwwDQYJKoZIhvcNAQELBQAwRDELMAkGA1UEBhMCVVMxFDAS
37610 BgNVBAoMC0FmZmlybVRydXN0MR8wHQYDVQQDDBZBZmZpcm1UcnVzdCBDb21tZXJjaWFsMB4XDTEw
37611 MDEyOTE0MDYwNloXDTMwMTIzMTE0MDYwNlowRDELMAkGA1UEBhMCVVMxFDASBgNVBAoMC0FmZmly
37612 bVRydXN0MR8wHQYDVQQDDBZBZmZpcm1UcnVzdCBDb21tZXJjaWFsMIIBIjANBgkqhkiG9w0BAQEF
37613 AAOCAQ8AMIIBCgKCAQEA9htPZwcroRX1BiLLHwGy43NFBkRJLLtJJRTWzsO3qyxPxkEylFf6Eqdb
37614 DuKPHx6GGaeqtS25Xw2Kwq+FNXkyLbscYjfysVtKPcrNcV/pQr6U6Mje+SJIZMblq8Yrba0F8PrV
37615 C8+a5fBQpIs7R6UjW3p6+DM/uO+Zl+MgwdYoic+U+7lF7eNAFxHUdPALMeIrJmqbTFeurCA+ukV6
37616 BfO9m2kVrn1OIGPENXY6BwLJN/3HR+7o8XYdcxXyl6S1yHp52UKqK39c/s4mT6NmgTWvRLpUHhww
37617 MmWd5jyTXlBOeuM61G7MGvv50jeuJCqrVwMiKA1JdX+3KNp1v47j3A55MQIDAQABo0IwQDAdBgNV
37618 HQ4EFgQUnZPGU4teyq8/nx4P5ZmVvCT2lI8wDwYDVR0TAQH/BAUwAwEB/zAOBgNVHQ8BAf8EBAMC
37619 AQYwDQYJKoZIhvcNAQELBQADggEBAFis9AQOzcAN/wr91LoWXym9e2iZWEnStB03TX8nfUYGXUPG
37620 hi4+c7ImfU+TqbbEKpqrIZcUsd6M06uJFdhrJNTxFq7YpFzUf1GO7RgBsZNjvbz4YYCanrHOQnDi
37621 qX0GJX0nof5v7LMeJNrjS1UaADs1tDvZ110w/YETifLCBivtZ8SOyUOyXGsViQK8YvxO8rUzqrJv
37622 0wqiUOP2O+guRMLbZjipM1ZI8W0bM40NjD9gN53Tym1+NH4Nn3J2ixufcv1SNUFFApYvHLKac0kh
37623 sUlHRUe072o0EclNmsxZt9YCnlpOZbWUrhvfKbAW8b8Angc6F2S1BLUjIZkKlTuXfO8=
37624 -----END CERTIFICATE-----
37625
37626 AffirmTrust Networking
37627 ======================
37628 -----BEGIN CERTIFICATE-----
37629 MIIDTDCCAjSgAwIBAgIIfE8EORzUmS0wDQYJKoZIhvcNAQEFBQAwRDELMAkGA1UEBhMCVVMxFDAS
37630 BgNVBAoMC0FmZmlybVRydXN0MR8wHQYDVQQDDBZBZmZpcm1UcnVzdCBOZXR3b3JraW5nMB4XDTEw
37631 MDEyOTE0MDgyNFoXDTMwMTIzMTE0MDgyNFowRDELMAkGA1UEBhMCVVMxFDASBgNVBAoMC0FmZmly
37632 bVRydXN0MR8wHQYDVQQDDBZBZmZpcm1UcnVzdCBOZXR3b3JraW5nMIIBIjANBgkqhkiG9w0BAQEF
37633 AAOCAQ8AMIIBCgKCAQEAtITMMxcua5Rsa2FSoOujz3mUTOWUgJnLVWREZY9nZOIG41w3SfYvm4SE
37634 Hi3yYJ0wTsyEheIszx6e/jarM3c1RNg1lho9Nuh6DtjVR6FqaYvZ/Ls6rnla1fTWcbuakCNrmreI
37635 dIcMHl+5ni36q1Mr3Lt2PpNMCAiMHqIjHNRqrSK6mQEubWXLviRmVSRLQESxG9fhwoXA3hA/Pe24
37636 /PHxI1Pcv2WXb9n5QHGNfb2V1M6+oF4nI979ptAmDgAp6zxG8D1gvz9Q0twmQVGeFDdCBKNwV6gb
37637 h+0t+nvujArjqWaJGctB+d1ENmHP4ndGyH329JKBNv3bNPFyfvMMFr20FQIDAQABo0IwQDAdBgNV
37638 HQ4EFgQUBx/S55zawm6iQLSwelAQUHTEyL0wDwYDVR0TAQH/BAUwAwEB/zAOBgNVHQ8BAf8EBAMC
37639 AQYwDQYJKoZIhvcNAQEFBQADggEBAIlXshZ6qML91tmbmzTCnLQyFE2npN/svqe++EPbkTfOtDIu
37640 UFUaNU52Q3Eg75N3ThVwLofDwR1t3Mu1J9QsVtFSUzpE0nPIxBsFZVpikpzuQY0x2+c06lkh1QF6
37641 12S4ZDnNye2v7UsDSKegmQGA3GWjNq5lWUhPgkvIZfFXHeVZLgo/bNjR9eUJtGxUAArgFU2HdW23
37642 WJZa3W3SAKD0m0i+wzekujbgfIeFlxoVot4uolu9rxj5kFDNcFn4J2dHy8egBzp90SxdbBk6ZrV9
37643 /ZFvgrG+CJPbFEfxojfHRZ48x3evZKiT3/Zpg4Jg8klCNO1aAFSFHBY2kgxc+qatv9s=
37644 -----END CERTIFICATE-----
37645
37646 AffirmTrust Premium
37647 ===================
37648 -----BEGIN CERTIFICATE-----
37649 MIIFRjCCAy6gAwIBAgIIbYwURrGmCu4wDQYJKoZIhvcNAQEMBQAwQTELMAkGA1UEBhMCVVMxFDAS
37650 BgNVBAoMC0FmZmlybVRydXN0MRwwGgYDVQQDDBNBZmZpcm1UcnVzdCBQcmVtaXVtMB4XDTEwMDEy
37651 OTE0MTAzNloXDTQwMTIzMTE0MTAzNlowQTELMAkGA1UEBhMCVVMxFDASBgNVBAoMC0FmZmlybVRy
37652 dXN0MRwwGgYDVQQDDBNBZmZpcm1UcnVzdCBQcmVtaXVtMIICIjANBgkqhkiG9w0BAQEFAAOCAg8A
37653 MIICCgKCAgEAxBLfqV/+Qd3d9Z+K4/as4Tx4mrzY8H96oDMq3I0gW64tb+eT2TZwamjPjlGjhVtn
37654 BKAQJG9dKILBl1fYSCkTtuG+kU3fhQxTGJoeJKJPj/CihQvL9Cl/0qRY7iZNyaqoe5rZ+jjeRFcV
37655 5fiMyNlI4g0WJx0eyIOFJbe6qlVBzAMiSy2RjYvmia9mx+n/K+k8rNrSs8PhaJyJ+HoAVt70VZVs
37656 +7pk3WKL3wt3MutizCaam7uqYoNMtAZ6MMgpv+0GTZe5HMQxK9VfvFMSF5yZVylmd2EhMQcuJUmd
37657 GPLu8ytxjLW6OQdJd/zvLpKQBY0tL3d770O/Nbua2Plzpyzy0FfuKE4mX4+QaAkvuPjcBukumj5R
37658 p9EixAqnOEhss/n/fauGV+O61oV4d7pD6kh/9ti+I20ev9E2bFhc8e6kGVQa9QPSdubhjL08s9NI
37659 S+LI+H+SqHZGnEJlPqQewQcDWkYtuJfzt9WyVSHvutxMAJf7FJUnM7/oQ0dG0giZFmA7mn7S5u04
37660 6uwBHjxIVkkJx0w3AJ6IDsBz4W9m6XJHMD4Q5QsDyZpCAGzFlH5hxIrff4IaC1nEWTJ3s7xgaVY5
37661 /bQGeyzWZDbZvUjthB9+pSKPKrhC9IK31FOQeE4tGv2Bb0TXOwF0lkLgAOIua+rF7nKsu7/+6qqo
37662 +Nz2snmKtmcCAwEAAaNCMEAwHQYDVR0OBBYEFJ3AZ6YMItkm9UWrpmVSESfYRaxjMA8GA1UdEwEB
37663 /wQFMAMBAf8wDgYDVR0PAQH/BAQDAgEGMA0GCSqGSIb3DQEBDAUAA4ICAQCzV00QYk465KzquByv
37664 MiPIs0laUZx2KI15qldGF9X1Uva3ROgIRL8YhNILgM3FEv0AVQVhh0HctSSePMTYyPtwni94loMg
37665 Nt58D2kTiKV1NpgIpsbfrM7jWNa3Pt668+s0QNiigfV4Py/VpfzZotReBA4Xrf5B8OWycvpEgjNC
37666 6C1Y91aMYj+6QrCcDFx+LmUmXFNPALJ4fqENmS2NuB2OosSw/WDQMKSOyARiqcTtNd56l+0OOF6S
37667 L5Nwpamcb6d9Ex1+xghIsV5n61EIJenmJWtSKZGc0jlzCFfemQa0W50QBuHCAKi4HEoCChTQwUHK
37668 +4w1IX2COPKpVJEZNZOUbWo6xbLQu4mGk+ibyQ86p3q4ofB4Rvr8Ny/lioTz3/4E2aFooC8k4gmV
37669 BtWVyuEklut89pMFu+1z6S3RdTnX5yTb2E5fQ4+e0BQ5v1VwSJlXMbSc7kqYA5YwH2AG7hsj/oFg
37670 IxpHYoWlzBk0gG+zrBrjn/B7SK3VAdlntqlyk+otZrWyuOQ9PLLvTIzq6we/qzWaVYa8GKa1qF60
37671 g2xraUDTn9zxw2lrueFtCfTxqlB2Cnp9ehehVZZCmTEJ3WARjQUwfuaORtGdFNrHF+QFlozEJLUb
37672 zxQHskD4o55BhrwE0GuWyCqANP2/7waj3VjFhT0+j/6eKeC2uAloGRwYQw==
37673 -----END CERTIFICATE-----
37674
37675 AffirmTrust Premium ECC
37676 =======================
37677 -----BEGIN CERTIFICATE-----
37678 MIIB/jCCAYWgAwIBAgIIdJclisc/elQwCgYIKoZIzj0EAwMwRTELMAkGA1UEBhMCVVMxFDASBgNV
37679 BAoMC0FmZmlybVRydXN0MSAwHgYDVQQDDBdBZmZpcm1UcnVzdCBQcmVtaXVtIEVDQzAeFw0xMDAx
37680 MjkxNDIwMjRaFw00MDEyMzExNDIwMjRaMEUxCzAJBgNVBAYTAlVTMRQwEgYDVQQKDAtBZmZpcm1U
37681 cnVzdDEgMB4GA1UEAwwXQWZmaXJtVHJ1c3QgUHJlbWl1bSBFQ0MwdjAQBgcqhkjOPQIBBgUrgQQA
37682 IgNiAAQNMF4bFZ0D0KF5Nbc6PJJ6yhUczWLznCZcBz3lVPqj1swS6vQUX+iOGasvLkjmrBhDeKzQ
37683 N8O9ss0s5kfiGuZjuD0uL3jET9v0D6RoTFVya5UdThhClXjMNzyR4ptlKymjQjBAMB0GA1UdDgQW
37684 BBSaryl6wBE1NSZRMADDav5A1a7WPDAPBgNVHRMBAf8EBTADAQH/MA4GA1UdDwEB/wQEAwIBBjAK
37685 BggqhkjOPQQDAwNnADBkAjAXCfOHiFBar8jAQr9HX/VsaobgxCd05DhT1wV/GzTjxi+zygk8N53X
37686 57hG8f2h4nECMEJZh0PUUd+60wkyWs6Iflc9nF9Ca/UHLbXwgpP5WW+uZPpY5Yse42O+tYHNbwKM
37687 eQ==
37688 -----END CERTIFICATE-----
37689
37690 Certum Trusted Network CA
37691 =========================
37692 -----BEGIN CERTIFICATE-----
37693 MIIDuzCCAqOgAwIBAgIDBETAMA0GCSqGSIb3DQEBBQUAMH4xCzAJBgNVBAYTAlBMMSIwIAYDVQQK
37694 ExlVbml6ZXRvIFRlY2hub2xvZ2llcyBTLkEuMScwJQYDVQQLEx5DZXJ0dW0gQ2VydGlmaWNhdGlv
37695 biBBdXRob3JpdHkxIjAgBgNVBAMTGUNlcnR1bSBUcnVzdGVkIE5ldHdvcmsgQ0EwHhcNMDgxMDIy
37696 MTIwNzM3WhcNMjkxMjMxMTIwNzM3WjB+MQswCQYDVQQGEwJQTDEiMCAGA1UEChMZVW5pemV0byBU
37697 ZWNobm9sb2dpZXMgUy5BLjEnMCUGA1UECxMeQ2VydHVtIENlcnRpZmljYXRpb24gQXV0aG9yaXR5
37698 MSIwIAYDVQQDExlDZXJ0dW0gVHJ1c3RlZCBOZXR3b3JrIENBMIIBIjANBgkqhkiG9w0BAQEFAAOC
37699 AQ8AMIIBCgKCAQEA4/t9o3K6wvDJFIf1awFO4W5AB7ptJ11/91sts1rHUV+rpDKmYYe2bg+G0jAC
37700 l/jXaVehGDldamR5xgFZrDwxSjh80gTSSyjoIF87B6LMTXPb865Px1bVWqeWifrzq2jUI4ZZJ88J
37701 J7ysbnKDHDBy3+Ci6dLhdHUZvSqeexVUBBvXQzmtVSjF4hq79MDkrjhJM8x2hZ85RdKknvISjFH4
37702 fOQtf/WsX+sWn7Et0brMkUJ3TCXJkDhv2/DM+44el1k+1WBO5gUo7Ul5E0u6SNsv+XLTOcr+H9g0
37703 cvW0QM8xAcPs3hEtF10fuFDRXhmnad4HMyjKUJX5p1TLVIZQRan5SQIDAQABo0IwQDAPBgNVHRMB
37704 Af8EBTADAQH/MB0GA1UdDgQWBBQIds3LB/8k9sXN7buQvOKEN0Z19zAOBgNVHQ8BAf8EBAMCAQYw
37705 DQYJKoZIhvcNAQEFBQADggEBAKaorSLOAT2mo/9i0Eidi15ysHhE49wcrwn9I0j6vSrEuVUEtRCj
37706 jSfeC4Jj0O7eDDd5QVsisrCaQVymcODU0HfLI9MA4GxWL+FpDQ3Zqr8hgVDZBqWo/5U30Kr+4rP1
37707 mS1FhIrlQgnXdAIv94nYmem8J9RHjboNRhx3zxSkHLmkMcScKHQDNP8zGSal6Q10tz6XxnboJ5aj
37708 Zt3hrvJBW8qYVoNzcOSGGtIxQbovvi0TWnZvTuhOgQ4/WwMioBK+ZlgRSssDxLQqKi2WF+A5VLxI
37709 03YnnZotBqbJ7DnSq9ufmgsnAjUpsUCV5/nonFWIGUbWtzT1fs45mtk48VH3Tyw=
37710 -----END CERTIFICATE-----
37711
37712 Certinomis - Autorité Racine
37713 =============================
37714 -----BEGIN CERTIFICATE-----
37715 MIIFnDCCA4SgAwIBAgIBATANBgkqhkiG9w0BAQUFADBjMQswCQYDVQQGEwJGUjETMBEGA1UEChMK
37716 Q2VydGlub21pczEXMBUGA1UECxMOMDAwMiA0MzM5OTg5MDMxJjAkBgNVBAMMHUNlcnRpbm9taXMg
37717 LSBBdXRvcml0w6kgUmFjaW5lMB4XDTA4MDkxNzA4Mjg1OVoXDTI4MDkxNzA4Mjg1OVowYzELMAkG
37718 A1UEBhMCRlIxEzARBgNVBAoTCkNlcnRpbm9taXMxFzAVBgNVBAsTDjAwMDIgNDMzOTk4OTAzMSYw
37719 JAYDVQQDDB1DZXJ0aW5vbWlzIC0gQXV0b3JpdMOpIFJhY2luZTCCAiIwDQYJKoZIhvcNAQEBBQAD
37720 ggIPADCCAgoCggIBAJ2Fn4bT46/HsmtuM+Cet0I0VZ35gb5j2CN2DpdUzZlMGvE5x4jYF1AMnmHa
37721 wE5V3udauHpOd4cN5bjr+p5eex7Ezyh0x5P1FMYiKAT5kcOrJ3NqDi5N8y4oH3DfVS9O7cdxbwly
37722 Lu3VMpfQ8Vh30WC8Tl7bmoT2R2FFK/ZQpn9qcSdIhDWerP5pqZ56XjUl+rSnSTV3lqc2W+HN3yNw
37723 2F1MpQiD8aYkOBOo7C+ooWfHpi2GR+6K/OybDnT0K0kCe5B1jPyZOQE51kqJ5Z52qz6WKDgmi92N
37724 jMD2AR5vpTESOH2VwnHu7XSu5DaiQ3XV8QCb4uTXzEIDS3h65X27uK4uIJPT5GHfceF2Z5c/tt9q
37725 c1pkIuVC28+BA5PY9OMQ4HL2AHCs8MF6DwV/zzRpRbWT5BnbUhYjBYkOjUjkJW+zeL9i9Qf6lSTC
37726 lrLooyPCXQP8w9PlfMl1I9f09bze5N/NgL+RiH2nE7Q5uiy6vdFrzPOlKO1Enn1So2+WLhl+HPNb
37727 xxaOu2B9d2ZHVIIAEWBsMsGoOBvrbpgT1u449fCfDu/+MYHB0iSVL1N6aaLwD4ZFjliCK0wi1F6g
37728 530mJ0jfJUaNSih8hp75mxpZuWW/Bd22Ql095gBIgl4g9xGC3srYn+Y3RyYe63j3YcNBZFgCQfna
37729 4NH4+ej9Uji29YnfAgMBAAGjWzBZMA8GA1UdEwEB/wQFMAMBAf8wDgYDVR0PAQH/BAQDAgEGMB0G
37730 A1UdDgQWBBQNjLZh2kS40RR9w759XkjwzspqsDAXBgNVHSAEEDAOMAwGCiqBegFWAgIAAQEwDQYJ
37731 KoZIhvcNAQEFBQADggIBACQ+YAZ+He86PtvqrxyaLAEL9MW12Ukx9F1BjYkMTv9sov3/4gbIOZ/x
37732 WqndIlgVqIrTseYyCYIDbNc/CMf4uboAbbnW/FIyXaR/pDGUu7ZMOH8oMDX/nyNTt7buFHAAQCva
37733 R6s0fl6nVjBhK4tDrP22iCj1a7Y+YEq6QpA0Z43q619FVDsXrIvkxmUP7tCMXWY5zjKn2BCXwH40
37734 nJ+U8/aGH88bc62UeYdocMMzpXDn2NU4lG9jeeu/Cg4I58UvD0KgKxRA/yHgBcUn4YQRE7rWhh1B
37735 CxMjidPJC+iKunqjo3M3NYB9Ergzd0A4wPpeMNLytqOx1qKVl4GbUu1pTP+A5FPbVFsDbVRfsbjv
37736 JL1vnxHDx2TCDyhihWZeGnuyt++uNckZM6i4J9szVb9o4XVIRFb7zdNIu0eJOqxp9YDG5ERQL1TE
37737 qkPFMTFYvZbF6nVsmnWxTfj3l/+WFvKXTej28xH5On2KOG4Ey+HTRRWqpdEdnV1j6CTmNhTih60b
37738 WfVEm/vXd3wfAXBioSAaosUaKPQhA+4u2cGA6rnZgtZbdsLLO7XSAPCjDuGtbkD326C00EauFddE
37739 wk01+dIL8hf2rGbVJLJP0RyZwG71fet0BLj5TXcJ17TPBzAJ8bgAVtkXFhYKK4bfjwEZGuW7gmP/
37740 vgt2Fl43N+bYdJeimUV5
37741 -----END CERTIFICATE-----
37742
37743 Root CA Generalitat Valenciana
37744 ==============================
37745 -----BEGIN CERTIFICATE-----
37746 MIIGizCCBXOgAwIBAgIEO0XlaDANBgkqhkiG9w0BAQUFADBoMQswCQYDVQQGEwJFUzEfMB0GA1UE
37747 ChMWR2VuZXJhbGl0YXQgVmFsZW5jaWFuYTEPMA0GA1UECxMGUEtJR1ZBMScwJQYDVQQDEx5Sb290
37748 IENBIEdlbmVyYWxpdGF0IFZhbGVuY2lhbmEwHhcNMDEwNzA2MTYyMjQ3WhcNMjEwNzAxMTUyMjQ3
37749 WjBoMQswCQYDVQQGEwJFUzEfMB0GA1UEChMWR2VuZXJhbGl0YXQgVmFsZW5jaWFuYTEPMA0GA1UE
37750 CxMGUEtJR1ZBMScwJQYDVQQDEx5Sb290IENBIEdlbmVyYWxpdGF0IFZhbGVuY2lhbmEwggEiMA0G
37751 CSqGSIb3DQEBAQUAA4IBDwAwggEKAoIBAQDGKqtXETcvIorKA3Qdyu0togu8M1JAJke+WmmmO3I2
37752 F0zo37i7L3bhQEZ0ZQKQUgi0/6iMweDHiVYQOTPvaLRfX9ptI6GJXiKjSgbwJ/BXufjpTjJ3Cj9B
37753 ZPPrZe52/lSqfR0grvPXdMIKX/UIKFIIzFVd0g/bmoGlu6GzwZTNVOAydTGRGmKy3nXiz0+J2ZGQ
37754 D0EbtFpKd71ng+CT516nDOeB0/RSrFOyA8dEJvt55cs0YFAQexvba9dHq198aMpunUEDEO5rmXte
37755 JajCq+TA81yc477OMUxkHl6AovWDfgzWyoxVjr7gvkkHD6MkQXpYHYTqWBLI4bft75PelAgxAgMB
37756 AAGjggM7MIIDNzAyBggrBgEFBQcBAQQmMCQwIgYIKwYBBQUHMAGGFmh0dHA6Ly9vY3NwLnBraS5n
37757 dmEuZXMwEgYDVR0TAQH/BAgwBgEB/wIBAjCCAjQGA1UdIASCAiswggInMIICIwYKKwYBBAG/VQIB
37758 ADCCAhMwggHoBggrBgEFBQcCAjCCAdoeggHWAEEAdQB0AG8AcgBpAGQAYQBkACAAZABlACAAQwBl
37759 AHIAdABpAGYAaQBjAGEAYwBpAPMAbgAgAFIAYQDtAHoAIABkAGUAIABsAGEAIABHAGUAbgBlAHIA
37760 YQBsAGkAdABhAHQAIABWAGEAbABlAG4AYwBpAGEAbgBhAC4ADQAKAEwAYQAgAEQAZQBjAGwAYQBy
37761 AGEAYwBpAPMAbgAgAGQAZQAgAFAAcgDhAGMAdABpAGMAYQBzACAAZABlACAAQwBlAHIAdABpAGYA
37762 aQBjAGEAYwBpAPMAbgAgAHEAdQBlACAAcgBpAGcAZQAgAGUAbAAgAGYAdQBuAGMAaQBvAG4AYQBt
37763 AGkAZQBuAHQAbwAgAGQAZQAgAGwAYQAgAHAAcgBlAHMAZQBuAHQAZQAgAEEAdQB0AG8AcgBpAGQA
37764 YQBkACAAZABlACAAQwBlAHIAdABpAGYAaQBjAGEAYwBpAPMAbgAgAHMAZQAgAGUAbgBjAHUAZQBu
37765 AHQAcgBhACAAZQBuACAAbABhACAAZABpAHIAZQBjAGMAaQDzAG4AIAB3AGUAYgAgAGgAdAB0AHAA
37766 OgAvAC8AdwB3AHcALgBwAGsAaQAuAGcAdgBhAC4AZQBzAC8AYwBwAHMwJQYIKwYBBQUHAgEWGWh0
37767 dHA6Ly93d3cucGtpLmd2YS5lcy9jcHMwHQYDVR0OBBYEFHs100DSHHgZZu90ECjcPk+yeAT8MIGV
37768 BgNVHSMEgY0wgYqAFHs100DSHHgZZu90ECjcPk+yeAT8oWykajBoMQswCQYDVQQGEwJFUzEfMB0G
37769 A1UEChMWR2VuZXJhbGl0YXQgVmFsZW5jaWFuYTEPMA0GA1UECxMGUEtJR1ZBMScwJQYDVQQDEx5S
37770 b290IENBIEdlbmVyYWxpdGF0IFZhbGVuY2lhbmGCBDtF5WgwDQYJKoZIhvcNAQEFBQADggEBACRh
37771 TvW1yEICKrNcda3FbcrnlD+laJWIwVTAEGmiEi8YPyVQqHxK6sYJ2fR1xkDar1CdPaUWu20xxsdz
37772 Ckj+IHLtb8zog2EWRpABlUt9jppSCS/2bxzkoXHPjCpaF3ODR00PNvsETUlR4hTJZGH71BTg9J63
37773 NI8KJr2XXPR5OkowGcytT6CYirQxlyric21+eLj4iIlPsSKRZEv1UN4D2+XFducTZnV+ZfsBn5OH
37774 iJ35Rld8TWCvmHMTI6QgkYH60GFmuH3Rr9ZvHmw96RH9qfmCIoaZM3Fa6hlXPZHNqcCjbgcTpsnt
37775 +GijnsNacgmHKNHEc8RzGF9QdRYxn7fofMM=
37776 -----END CERTIFICATE-----
37777
37778 A-Trust-nQual-03
37779 ================
37780 -----BEGIN CERTIFICATE-----
37781 MIIDzzCCAregAwIBAgIDAWweMA0GCSqGSIb3DQEBBQUAMIGNMQswCQYDVQQGEwJBVDFIMEYGA1UE
37782 Cgw/QS1UcnVzdCBHZXMuIGYuIFNpY2hlcmhlaXRzc3lzdGVtZSBpbSBlbGVrdHIuIERhdGVudmVy
37783 a2VociBHbWJIMRkwFwYDVQQLDBBBLVRydXN0LW5RdWFsLTAzMRkwFwYDVQQDDBBBLVRydXN0LW5R
37784 dWFsLTAzMB4XDTA1MDgxNzIyMDAwMFoXDTE1MDgxNzIyMDAwMFowgY0xCzAJBgNVBAYTAkFUMUgw
37785 RgYDVQQKDD9BLVRydXN0IEdlcy4gZi4gU2ljaGVyaGVpdHNzeXN0ZW1lIGltIGVsZWt0ci4gRGF0
37786 ZW52ZXJrZWhyIEdtYkgxGTAXBgNVBAsMEEEtVHJ1c3QtblF1YWwtMDMxGTAXBgNVBAMMEEEtVHJ1
37787 c3QtblF1YWwtMDMwggEiMA0GCSqGSIb3DQEBAQUAA4IBDwAwggEKAoIBAQCtPWFuA/OQO8BBC4SA
37788 zewqo51ru27CQoT3URThoKgtUaNR8t4j8DRE/5TrzAUjlUC5B3ilJfYKvUWG6Nm9wASOhURh73+n
37789 yfrBJcyFLGM/BWBzSQXgYHiVEEvc+RFZznF/QJuKqiTfC0Li21a8StKlDJu3Qz7dg9MmEALP6iPE
37790 SU7l0+m0iKsMrmKS1GWH2WrX9IWf5DMiJaXlyDO6w8dB3F/GaswADm0yqLaHNgBid5seHzTLkDx4
37791 iHQF63n1k3Flyp3HaxgtPVxO59X4PzF9j4fsCiIvI+n+u33J4PTs63zEsMMtYrWacdaxaujs2e3V
37792 cuy+VwHOBVWf3tFgiBCzAgMBAAGjNjA0MA8GA1UdEwEB/wQFMAMBAf8wEQYDVR0OBAoECERqlWdV
37793 eRFPMA4GA1UdDwEB/wQEAwIBBjANBgkqhkiG9w0BAQUFAAOCAQEAVdRU0VlIXLOThaq/Yy/kgM40
37794 ozRiPvbY7meIMQQDbwvUB/tOdQ/TLtPAF8fGKOwGDREkDg6lXb+MshOWcdzUzg4NCmgybLlBMRmr
37795 sQd7TZjTXLDR8KdCoLXEjq/+8T/0709GAHbrAvv5ndJAlseIOrifEXnzgGWovR/TeIGgUUw3tKZd
37796 JXDRZslo+S4RFGjxVJgIrCaSD96JntT6s3kr0qN51OyLrIdTaEJMUVF0HhsnLuP1Hyl0Te2v9+GS
37797 mYHovjrHF1D2t8b8m7CKa9aIA5GPBnc6hQLdmNVDeD/GMBWsm2vLV7eJUYs66MmEDNuxUCAKGkq6
37798 ahq97BvIxYSazQ==
37799 -----END CERTIFICATE-----
37800
37801 TWCA Root Certification Authority
37802 =================================
37803 -----BEGIN CERTIFICATE-----
37804 MIIDezCCAmOgAwIBAgIBATANBgkqhkiG9w0BAQUFADBfMQswCQYDVQQGEwJUVzESMBAGA1UECgwJ
37805 VEFJV0FOLUNBMRAwDgYDVQQLDAdSb290IENBMSowKAYDVQQDDCFUV0NBIFJvb3QgQ2VydGlmaWNh
37806 dGlvbiBBdXRob3JpdHkwHhcNMDgwODI4MDcyNDMzWhcNMzAxMjMxMTU1OTU5WjBfMQswCQYDVQQG
37807 EwJUVzESMBAGA1UECgwJVEFJV0FOLUNBMRAwDgYDVQQLDAdSb290IENBMSowKAYDVQQDDCFUV0NB
37808 IFJvb3QgQ2VydGlmaWNhdGlvbiBBdXRob3JpdHkwggEiMA0GCSqGSIb3DQEBAQUAA4IBDwAwggEK
37809 AoIBAQCwfnK4pAOU5qfeCTiRShFAh6d8WWQUe7UREN3+v9XAu1bihSX0NXIP+FPQQeFEAcK0HMMx
37810 QhZHhTMidrIKbw/lJVBPhYa+v5guEGcevhEFhgWQxFnQfHgQsIBct+HHK3XLfJ+utdGdIzdjp9xC
37811 oi2SBBtQwXu4PhvJVgSLL1KbralW6cH/ralYhzC2gfeXRfwZVzsrb+RH9JlF/h3x+JejiB03HFyP
37812 4HYlmlD4oFT/RJB2I9IyxsOrBr/8+7/zrX2SYgJbKdM1o5OaQ2RgXbL6Mv87BK9NQGr5x+PvI/1r
37813 y+UPizgN7gr8/g+YnzAx3WxSZfmLgb4i4RxYA7qRG4kHAgMBAAGjQjBAMA4GA1UdDwEB/wQEAwIB
37814 BjAPBgNVHRMBAf8EBTADAQH/MB0GA1UdDgQWBBRqOFsmjd6LWvJPelSDGRjjCDWmujANBgkqhkiG
37815 9w0BAQUFAAOCAQEAPNV3PdrfibqHDAhUaiBQkr6wQT25JmSDCi/oQMCXKCeCMErJk/9q56YAf4lC
37816 mtYR5VPOL8zy2gXE/uJQxDqGfczafhAJO5I1KlOy/usrBdlsXebQ79NqZp4VKIV66IIArB6nCWlW
37817 QtNoURi+VJq/REG6Sb4gumlc7rh3zc5sH62Dlhh9DrUUOYTxKOkto557HnpyWoOzeW/vtPzQCqVY
37818 T0bf+215WfKEIlKuD8z7fDvnaspHYcN6+NOSBB+4IIThNlQWx0DeO4pz3N/GCUzf7Nr/1FNCocny
37819 Yh0igzyXxfkZYiesZSLX0zzG5Y6yU8xJzrww/nsOM5D77dIUkR8Hrw==
37820 -----END CERTIFICATE-----
37821
37822 Security Communication RootCA2
37823 ==============================
37824 -----BEGIN CERTIFICATE-----
37825 MIIDdzCCAl+gAwIBAgIBADANBgkqhkiG9w0BAQsFADBdMQswCQYDVQQGEwJKUDElMCMGA1UEChMc
37826 U0VDT00gVHJ1c3QgU3lzdGVtcyBDTy4sTFRELjEnMCUGA1UECxMeU2VjdXJpdHkgQ29tbXVuaWNh
37827 dGlvbiBSb290Q0EyMB4XDTA5MDUyOTA1MDAzOVoXDTI5MDUyOTA1MDAzOVowXTELMAkGA1UEBhMC
37828 SlAxJTAjBgNVBAoTHFNFQ09NIFRydXN0IFN5c3RlbXMgQ08uLExURC4xJzAlBgNVBAsTHlNlY3Vy
37829 aXR5IENvbW11bmljYXRpb24gUm9vdENBMjCCASIwDQYJKoZIhvcNAQEBBQADggEPADCCAQoCggEB
37830 ANAVOVKxUrO6xVmCxF1SrjpDZYBLx/KWvNs2l9amZIyoXvDjChz335c9S672XewhtUGrzbl+dp++
37831 +T42NKA7wfYxEUV0kz1XgMX5iZnK5atq1LXaQZAQwdbWQonCv/Q4EpVMVAX3NuRFg3sUZdbcDE3R
37832 3n4MqzvEFb46VqZab3ZpUql6ucjrappdUtAtCms1FgkQhNBqyjoGADdH5H5XTz+L62e4iKrFvlNV
37833 spHEfbmwhRkGeC7bYRr6hfVKkaHnFtWOojnflLhwHyg/i/xAXmODPIMqGplrz95Zajv8bxbXH/1K
37834 EOtOghY6rCcMU/Gt1SSwawNQwS08Ft1ENCcadfsCAwEAAaNCMEAwHQYDVR0OBBYEFAqFqXdlBZh8
37835 QIH4D5csOPEK7DzPMA4GA1UdDwEB/wQEAwIBBjAPBgNVHRMBAf8EBTADAQH/MA0GCSqGSIb3DQEB
37836 CwUAA4IBAQBMOqNErLlFsceTfsgLCkLfZOoc7llsCLqJX2rKSpWeeo8HxdpFcoJxDjrSzG+ntKEj
37837 u/Ykn8sX/oymzsLS28yN/HH8AynBbF0zX2S2ZTuJbxh2ePXcokgfGT+Ok+vx+hfuzU7jBBJV1uXk
37838 3fs+BXziHV7Gp7yXT2g69ekuCkO2r1dcYmh8t/2jioSgrGK+KwmHNPBqAbubKVY8/gA3zyNs8U6q
37839 tnRGEmyR7jTV7JqR50S+kDFy1UkC9gLl9B/rfNmWVan/7Ir5mUf/NVoCqgTLiluHcSmRvaS0eg29
37840 mvVXIwAHIRc/SjnRBUkLp7Y3gaVdjKozXoEofKd9J+sAro03
37841 -----END CERTIFICATE-----
37842
37843 EC-ACC
37844 ======
37845 -----BEGIN CERTIFICATE-----
37846 MIIFVjCCBD6gAwIBAgIQ7is969Qh3hSoYqwE893EATANBgkqhkiG9w0BAQUFADCB8zELMAkGA1UE
37847 BhMCRVMxOzA5BgNVBAoTMkFnZW5jaWEgQ2F0YWxhbmEgZGUgQ2VydGlmaWNhY2lvIChOSUYgUS0w
37848 ODAxMTc2LUkpMSgwJgYDVQQLEx9TZXJ2ZWlzIFB1YmxpY3MgZGUgQ2VydGlmaWNhY2lvMTUwMwYD
37849 VQQLEyxWZWdldSBodHRwczovL3d3dy5jYXRjZXJ0Lm5ldC92ZXJhcnJlbCAoYykwMzE1MDMGA1UE
37850 CxMsSmVyYXJxdWlhIEVudGl0YXRzIGRlIENlcnRpZmljYWNpbyBDYXRhbGFuZXMxDzANBgNVBAMT
37851 BkVDLUFDQzAeFw0wMzAxMDcyMzAwMDBaFw0zMTAxMDcyMjU5NTlaMIHzMQswCQYDVQQGEwJFUzE7
37852 MDkGA1UEChMyQWdlbmNpYSBDYXRhbGFuYSBkZSBDZXJ0aWZpY2FjaW8gKE5JRiBRLTA4MDExNzYt
37853 SSkxKDAmBgNVBAsTH1NlcnZlaXMgUHVibGljcyBkZSBDZXJ0aWZpY2FjaW8xNTAzBgNVBAsTLFZl
37854 Z2V1IGh0dHBzOi8vd3d3LmNhdGNlcnQubmV0L3ZlcmFycmVsIChjKTAzMTUwMwYDVQQLEyxKZXJh
37855 cnF1aWEgRW50aXRhdHMgZGUgQ2VydGlmaWNhY2lvIENhdGFsYW5lczEPMA0GA1UEAxMGRUMtQUND
37856 MIIBIjANBgkqhkiG9w0BAQEFAAOCAQ8AMIIBCgKCAQEAsyLHT+KXQpWIR4NA9h0X84NzJB5R85iK
37857 w5K4/0CQBXCHYMkAqbWUZRkiFRfCQ2xmRJoNBD45b6VLeqpjt4pEndljkYRm4CgPukLjbo73FCeT
37858 ae6RDqNfDrHrZqJyTxIThmV6PttPB/SnCWDaOkKZx7J/sxaVHMf5NLWUhdWZXqBIoH7nF2W4onW4
37859 HvPlQn2v7fOKSGRdghST2MDk/7NQcvJ29rNdQlB50JQ+awwAvthrDk4q7D7SzIKiGGUzE3eeml0a
37860 E9jD2z3Il3rucO2n5nzbcc8tlGLfbdb1OL4/pYUKGbio2Al1QnDE6u/LDsg0qBIimAy4E5S2S+zw
37861 0JDnJwIDAQABo4HjMIHgMB0GA1UdEQQWMBSBEmVjX2FjY0BjYXRjZXJ0Lm5ldDAPBgNVHRMBAf8E
37862 BTADAQH/MA4GA1UdDwEB/wQEAwIBBjAdBgNVHQ4EFgQUoMOLRKo3pUW/l4Ba0fF4opvpXY0wfwYD
37863 VR0gBHgwdjB0BgsrBgEEAfV4AQMBCjBlMCwGCCsGAQUFBwIBFiBodHRwczovL3d3dy5jYXRjZXJ0
37864 Lm5ldC92ZXJhcnJlbDA1BggrBgEFBQcCAjApGidWZWdldSBodHRwczovL3d3dy5jYXRjZXJ0Lm5l
37865 dC92ZXJhcnJlbCAwDQYJKoZIhvcNAQEFBQADggEBAKBIW4IB9k1IuDlVNZyAelOZ1Vr/sXE7zDkJ
37866 lF7W2u++AVtd0x7Y/X1PzaBB4DSTv8vihpw3kpBWHNzrKQXlxJ7HNd+KDM3FIUPpqojlNcAZQmNa
37867 Al6kSBg6hW/cnbw/nZzBh7h6YQjpdwt/cKt63dmXLGQehb+8dJahw3oS7AwaboMMPOhyRp/7SNVe
37868 l+axofjk70YllJyJ22k4vuxcDlbHZVHlUIiIv0LVKz3l+bqeLrPK9HOSAgu+TGbrIP65y7WZf+a2
37869 E/rKS03Z7lNGBjvGTq2TWoF+bCpLagVFjPIhpDGQh2xlnJ2lYJU6Un/10asIbvPuW/mIPX64b24D
37870 5EI=
37871 -----END CERTIFICATE-----
37872
37873 Hellenic Academic and Research Institutions RootCA 2011
37874 =======================================================
37875 -----BEGIN CERTIFICATE-----
37876 MIIEMTCCAxmgAwIBAgIBADANBgkqhkiG9w0BAQUFADCBlTELMAkGA1UEBhMCR1IxRDBCBgNVBAoT
37877 O0hlbGxlbmljIEFjYWRlbWljIGFuZCBSZXNlYXJjaCBJbnN0aXR1dGlvbnMgQ2VydC4gQXV0aG9y
37878 aXR5MUAwPgYDVQQDEzdIZWxsZW5pYyBBY2FkZW1pYyBhbmQgUmVzZWFyY2ggSW5zdGl0dXRpb25z
37879 IFJvb3RDQSAyMDExMB4XDTExMTIwNjEzNDk1MloXDTMxMTIwMTEzNDk1MlowgZUxCzAJBgNVBAYT
37880 AkdSMUQwQgYDVQQKEztIZWxsZW5pYyBBY2FkZW1pYyBhbmQgUmVzZWFyY2ggSW5zdGl0dXRpb25z
37881 IENlcnQuIEF1dGhvcml0eTFAMD4GA1UEAxM3SGVsbGVuaWMgQWNhZGVtaWMgYW5kIFJlc2VhcmNo
37882 IEluc3RpdHV0aW9ucyBSb290Q0EgMjAxMTCCASIwDQYJKoZIhvcNAQEBBQADggEPADCCAQoCggEB
37883 AKlTAOMupvaO+mDYLZU++CwqVE7NuYRhlFhPjz2L5EPzdYmNUeTDN9KKiE15HrcS3UN4SoqS5tdI
37884 1Q+kOilENbgH9mgdVc04UfCMJDGFr4PJfel3r+0ae50X+bOdOFAPplp5kYCvN66m0zH7tSYJnTxa
37885 71HFK9+WXesyHgLacEnsbgzImjeN9/E2YEsmLIKe0HjzDQ9jpFEw4fkrJxIH2Oq9GGKYsFk3fb7u
37886 8yBRQlqD75O6aRXxYp2fmTmCobd0LovUxQt7L/DICto9eQqakxylKHJzkUOap9FNhYS5qXSPFEDH
37887 3N6sQWRstBmbAmNtJGSPRLIl6s5ddAxjMlyNh+UCAwEAAaOBiTCBhjAPBgNVHRMBAf8EBTADAQH/
37888 MAsGA1UdDwQEAwIBBjAdBgNVHQ4EFgQUppFC/RNhSiOeCKQp5dgTBCPuQSUwRwYDVR0eBEAwPqA8
37889 MAWCAy5ncjAFggMuZXUwBoIELmVkdTAGggQub3JnMAWBAy5ncjAFgQMuZXUwBoEELmVkdTAGgQQu
37890 b3JnMA0GCSqGSIb3DQEBBQUAA4IBAQAf73lB4XtuP7KMhjdCSk4cNx6NZrokgclPEg8hwAOXhiVt
37891 XdMiKahsog2p6z0GW5k6x8zDmjR/qw7IThzh+uTczQ2+vyT+bOdrwg3IBp5OjWEopmr95fZi6hg8
37892 TqBTnbI6nOulnJEWtk2C4AwFSKls9cz4y51JtPACpf1wA+2KIaWuE4ZJwzNzvoc7dIsXRSZMFpGD
37893 /md9zU1jZ/rzAxKWeAaNsWftjj++n08C9bMJL/NMh98qy5V8AcysNnq/onN694/BtZqhFLKPM58N
37894 7yLcZnuEvUUXBj08yrl3NI/K6s8/MT7jiOOASSXIl7WdmplNsDz4SgCbZN2fOUvRJ9e4
37895 -----END CERTIFICATE-----
37896
37897 Actalis Authentication Root CA
37898 ==============================
37899 -----BEGIN CERTIFICATE-----
37900 MIIFuzCCA6OgAwIBAgIIVwoRl0LE48wwDQYJKoZIhvcNAQELBQAwazELMAkGA1UEBhMCSVQxDjAM
37901 BgNVBAcMBU1pbGFuMSMwIQYDVQQKDBpBY3RhbGlzIFMucC5BLi8wMzM1ODUyMDk2NzEnMCUGA1UE
37902 AwweQWN0YWxpcyBBdXRoZW50aWNhdGlvbiBSb290IENBMB4XDTExMDkyMjExMjIwMloXDTMwMDky
37903 MjExMjIwMlowazELMAkGA1UEBhMCSVQxDjAMBgNVBAcMBU1pbGFuMSMwIQYDVQQKDBpBY3RhbGlz
37904 IFMucC5BLi8wMzM1ODUyMDk2NzEnMCUGA1UEAwweQWN0YWxpcyBBdXRoZW50aWNhdGlvbiBSb290
37905 IENBMIICIjANBgkqhkiG9w0BAQEFAAOCAg8AMIICCgKCAgEAp8bEpSmkLO/lGMWwUKNvUTufClrJ
37906 wkg4CsIcoBh/kbWHuUA/3R1oHwiD1S0eiKD4j1aPbZkCkpAW1V8IbInX4ay8IMKx4INRimlNAJZa
37907 by/ARH6jDuSRzVju3PvHHkVH3Se5CAGfpiEd9UEtL0z9KK3giq0itFZljoZUj5NDKd45RnijMCO6
37908 zfB9E1fAXdKDa0hMxKufgFpbOr3JpyI/gCczWw63igxdBzcIy2zSekciRDXFzMwujt0q7bd9Zg1f
37909 YVEiVRvjRuPjPdA1YprbrxTIW6HMiRvhMCb8oJsfgadHHwTrozmSBp+Z07/T6k9QnBn+locePGX2
37910 oxgkg4YQ51Q+qDp2JE+BIcXjDwL4k5RHILv+1A7TaLndxHqEguNTVHnd25zS8gebLra8Pu2Fbe8l
37911 EfKXGkJh90qX6IuxEAf6ZYGyojnP9zz/GPvG8VqLWeICrHuS0E4UT1lF9gxeKF+w6D9Fz8+vm2/7
37912 hNN3WpVvrJSEnu68wEqPSpP4RCHiMUVhUE4Q2OM1fEwZtN4Fv6MGn8i1zeQf1xcGDXqVdFUNaBr8
37913 EBtiZJ1t4JWgw5QHVw0U5r0F+7if5t+L4sbnfpb2U8WANFAoWPASUHEXMLrmeGO89LKtmyuy/uE5
37914 jF66CyCU3nuDuP/jVo23Eek7jPKxwV2dpAtMK9myGPW1n0sCAwEAAaNjMGEwHQYDVR0OBBYEFFLY
37915 iDrIn3hm7YnzezhwlMkCAjbQMA8GA1UdEwEB/wQFMAMBAf8wHwYDVR0jBBgwFoAUUtiIOsifeGbt
37916 ifN7OHCUyQICNtAwDgYDVR0PAQH/BAQDAgEGMA0GCSqGSIb3DQEBCwUAA4ICAQALe3KHwGCmSUyI
37917 WOYdiPcUZEim2FgKDk8TNd81HdTtBjHIgT5q1d07GjLukD0R0i70jsNjLiNmsGe+b7bAEzlgqqI0
37918 JZN1Ut6nna0Oh4lScWoWPBkdg/iaKWW+9D+a2fDzWochcYBNy+A4mz+7+uAwTc+G02UQGRjRlwKx
37919 K3JCaKygvU5a2hi/a5iB0P2avl4VSM0RFbnAKVy06Ij3Pjaut2L9HmLecHgQHEhb2rykOLpn7VU+
37920 Xlff1ANATIGk0k9jpwlCCRT8AKnCgHNPLsBA2RF7SOp6AsDT6ygBJlh0wcBzIm2Tlf05fbsq4/aC
37921 4yyXX04fkZT6/iyj2HYauE2yOE+b+h1IYHkm4vP9qdCa6HCPSXrW5b0KDtst842/6+OkfcvHlXHo
37922 2qN8xcL4dJIEG4aspCJTQLas/kx2z/uUMsA1n3Y/buWQbqCmJqK4LL7RK4X9p2jIugErsWx0Hbhz
37923 lefut8cl8ABMALJ+tguLHPPAUJ4lueAI3jZm/zel0btUZCzJJ7VLkn5l/9Mt4blOvH+kQSGQQXem
37924 OR/qnuOf0GZvBeyqdn6/axag67XH/JJULysRJyU3eExRarDzzFhdFPFqSBX/wge2sY0PjlxQRrM9
37925 vwGYT7JZVEc+NHt4bVaTLnPqZih4zR0Uv6CPLy64Lo7yFIrM6bV8+2ydDKXhlg==
37926 -----END CERTIFICATE-----
37927
37928 Trustis FPS Root CA
37929 ===================
37930 -----BEGIN CERTIFICATE-----
37931 MIIDZzCCAk+gAwIBAgIQGx+ttiD5JNM2a/fH8YygWTANBgkqhkiG9w0BAQUFADBFMQswCQYDVQQG
37932 EwJHQjEYMBYGA1UEChMPVHJ1c3RpcyBMaW1pdGVkMRwwGgYDVQQLExNUcnVzdGlzIEZQUyBSb290
37933 IENBMB4XDTAzMTIyMzEyMTQwNloXDTI0MDEyMTExMzY1NFowRTELMAkGA1UEBhMCR0IxGDAWBgNV
37934 BAoTD1RydXN0aXMgTGltaXRlZDEcMBoGA1UECxMTVHJ1c3RpcyBGUFMgUm9vdCBDQTCCASIwDQYJ
37935 KoZIhvcNAQEBBQADggEPADCCAQoCggEBAMVQe547NdDfxIzNjpvto8A2mfRC6qc+gIMPpqdZh8mQ
37936 RUN+AOqGeSoDvT03mYlmt+WKVoaTnGhLaASMk5MCPjDSNzoiYYkchU59j9WvezX2fihHiTHcDnlk
37937 H5nSW7r+f2C/revnPDgpai/lkQtV/+xvWNUtyd5MZnGPDNcE2gfmHhjjvSkCqPoc4Vu5g6hBSLwa
37938 cY3nYuUtsuvffM/bq1rKMfFMIvMFE/eC+XN5DL7XSxzA0RU8k0Fk0ea+IxciAIleH2ulrG6nS4zt
37939 o3Lmr2NNL4XSFDWaLk6M6jKYKIahkQlBOrTh4/L68MkKokHdqeMDx4gVOxzUGpTXn2RZEm0CAwEA
37940 AaNTMFEwDwYDVR0TAQH/BAUwAwEB/zAfBgNVHSMEGDAWgBS6+nEleYtXQSUhhgtx67JkDoshZzAd
37941 BgNVHQ4EFgQUuvpxJXmLV0ElIYYLceuyZA6LIWcwDQYJKoZIhvcNAQEFBQADggEBAH5Y//01GX2c
37942 GE+esCu8jowU/yyg2kdbw++BLa8F6nRIW/M+TgfHbcWzk88iNVy2P3UnXwmWzaD+vkAMXBJV+JOC
37943 yinpXj9WV4s4NvdFGkwozZ5BuO1WTISkQMi4sKUraXAEasP41BIy+Q7DsdwyhEQsb8tGD+pmQQ9P
37944 8Vilpg0ND2HepZ5dfWWhPBfnqFVO76DH7cZEf1T1o+CP8HxVIo8ptoGj4W1OLBuAZ+ytIJ8MYmHV
37945 l/9D7S3B2l0pKoU/rGXuhg8FjZBf3+6f9L/uHfuY5H+QK4R4EA5sSVPvFVtlRkpdr7r7OnIdzfYl
37946 iB6XzCGcKQENZetX2fNXlrtIzYE=
37947 -----END CERTIFICATE-----
37948
37949 StartCom Certification Authority
37950 ================================
37951 -----BEGIN CERTIFICATE-----
37952 MIIHhzCCBW+gAwIBAgIBLTANBgkqhkiG9w0BAQsFADB9MQswCQYDVQQGEwJJTDEWMBQGA1UEChMN
37953 U3RhcnRDb20gTHRkLjErMCkGA1UECxMiU2VjdXJlIERpZ2l0YWwgQ2VydGlmaWNhdGUgU2lnbmlu
37954 ZzEpMCcGA1UEAxMgU3RhcnRDb20gQ2VydGlmaWNhdGlvbiBBdXRob3JpdHkwHhcNMDYwOTE3MTk0
37955 NjM3WhcNMzYwOTE3MTk0NjM2WjB9MQswCQYDVQQGEwJJTDEWMBQGA1UEChMNU3RhcnRDb20gTHRk
37956 LjErMCkGA1UECxMiU2VjdXJlIERpZ2l0YWwgQ2VydGlmaWNhdGUgU2lnbmluZzEpMCcGA1UEAxMg
37957 U3RhcnRDb20gQ2VydGlmaWNhdGlvbiBBdXRob3JpdHkwggIiMA0GCSqGSIb3DQEBAQUAA4ICDwAw
37958 ggIKAoICAQDBiNsJvGxGfHiflXu1M5DycmLWwTYgIiRezul38kMKogZkpMyONvg45iPwbm2xPN1y
37959 o4UcodM9tDMr0y+v/uqwQVlntsQGfQqedIXWeUyAN3rfOQVSWff0G0ZDpNKFhdLDcfN1YjS6LIp/
37960 Ho/u7TTQEceWzVI9ujPW3U3eCztKS5/CJi/6tRYccjV3yjxd5srhJosaNnZcAdt0FCX+7bWgiA/d
37961 eMotHweXMAEtcnn6RtYTKqi5pquDSR3l8u/d5AGOGAqPY1MWhWKpDhk6zLVmpsJrdAfkK+F2PrRt
37962 2PZE4XNiHzvEvqBTViVsUQn3qqvKv3b9bZvzndu/PWa8DFaqr5hIlTpL36dYUNk4dalb6kMMAv+Z
37963 6+hsTXBbKWWc3apdzK8BMewM69KN6Oqce+Zu9ydmDBpI125C4z/eIT574Q1w+2OqqGwaVLRcJXrJ
37964 osmLFqa7LH4XXgVNWG4SHQHuEhANxjJ/GP/89PrNbpHoNkm+Gkhpi8KWTRoSsmkXwQqQ1vp5Iki/
37965 untp+HDH+no32NgN0nZPV/+Qt+OR0t3vwmC3Zzrd/qqc8NSLf3Iizsafl7b4r4qgEKjZ+xjGtrVc
37966 UjyJthkqcwEKDwOzEmDyei+B26Nu/yYwl/WL3YlXtq09s68rxbd2AvCl1iuahhQqcvbjM4xdCUsT
37967 37uMdBNSSwIDAQABo4ICEDCCAgwwDwYDVR0TAQH/BAUwAwEB/zAOBgNVHQ8BAf8EBAMCAQYwHQYD
37968 VR0OBBYEFE4L7xqkQFulF2mHMMo0aEPQQa7yMB8GA1UdIwQYMBaAFE4L7xqkQFulF2mHMMo0aEPQ
37969 Qa7yMIIBWgYDVR0gBIIBUTCCAU0wggFJBgsrBgEEAYG1NwEBATCCATgwLgYIKwYBBQUHAgEWImh0
37970 dHA6Ly93d3cuc3RhcnRzc2wuY29tL3BvbGljeS5wZGYwNAYIKwYBBQUHAgEWKGh0dHA6Ly93d3cu
37971 c3RhcnRzc2wuY29tL2ludGVybWVkaWF0ZS5wZGYwgc8GCCsGAQUFBwICMIHCMCcWIFN0YXJ0IENv
37972 bW1lcmNpYWwgKFN0YXJ0Q29tKSBMdGQuMAMCAQEagZZMaW1pdGVkIExpYWJpbGl0eSwgcmVhZCB0
37973 aGUgc2VjdGlvbiAqTGVnYWwgTGltaXRhdGlvbnMqIG9mIHRoZSBTdGFydENvbSBDZXJ0aWZpY2F0
37974 aW9uIEF1dGhvcml0eSBQb2xpY3kgYXZhaWxhYmxlIGF0IGh0dHA6Ly93d3cuc3RhcnRzc2wuY29t
37975 L3BvbGljeS5wZGYwEQYJYIZIAYb4QgEBBAQDAgAHMDgGCWCGSAGG+EIBDQQrFilTdGFydENvbSBG
37976 cmVlIFNTTCBDZXJ0aWZpY2F0aW9uIEF1dGhvcml0eTANBgkqhkiG9w0BAQsFAAOCAgEAjo/n3JR5
37977 fPGFf59Jb2vKXfuM/gTFwWLRfUKKvFO3lANmMD+x5wqnUCBVJX92ehQN6wQOQOY+2IirByeDqXWm
37978 N3PH/UvSTa0XQMhGvjt/UfzDtgUx3M2FIk5xt/JxXrAaxrqTi3iSSoX4eA+D/i+tLPfkpLst0OcN
37979 Org+zvZ49q5HJMqjNTbOx8aHmNrs++myziebiMMEofYLWWivydsQD032ZGNcpRJvkrKTlMeIFw6T
37980 tn5ii5B/q06f/ON1FE8qMt9bDeD1e5MNq6HPh+GlBEXoPBKlCcWw0bdT82AUuoVpaiF8H3VhFyAX
37981 e2w7QSlc4axa0c2Mm+tgHRns9+Ww2vl5GKVFP0lDV9LdJNUso/2RjSe15esUBppMeyG7Oq0wBhjA
37982 2MFrLH9ZXF2RsXAiV+uKa0hK1Q8p7MZAwC+ITGgBF3f0JBlPvfrhsiAhS90a2Cl9qrjeVOwhVYBs
37983 HvUwyKMQ5bLmKhQxw4UtjJixhlpPiVktucf3HMiKf8CdBUrmQk9io20ppB+Fq9vlgcitKj1MXVuE
37984 JnHEhV5xJMqlG2zYYdMa4FTbzrqpMrUi9nNBCV24F10OD5mQ1kfabwo6YigUZ4LZ8dCAWZvLMdib
37985 D4x3TrVoivJs9iQOLWxwxXPR3hTQcY+203sC9uO41Alua551hDnmfyWl8kgAwKQB2j8=
37986 -----END CERTIFICATE-----
37987
37988 StartCom Certification Authority G2
37989 ===================================
37990 -----BEGIN CERTIFICATE-----
37991 MIIFYzCCA0ugAwIBAgIBOzANBgkqhkiG9w0BAQsFADBTMQswCQYDVQQGEwJJTDEWMBQGA1UEChMN
37992 U3RhcnRDb20gTHRkLjEsMCoGA1UEAxMjU3RhcnRDb20gQ2VydGlmaWNhdGlvbiBBdXRob3JpdHkg
37993 RzIwHhcNMTAwMTAxMDEwMDAxWhcNMzkxMjMxMjM1OTAxWjBTMQswCQYDVQQGEwJJTDEWMBQGA1UE
37994 ChMNU3RhcnRDb20gTHRkLjEsMCoGA1UEAxMjU3RhcnRDb20gQ2VydGlmaWNhdGlvbiBBdXRob3Jp
37995 dHkgRzIwggIiMA0GCSqGSIb3DQEBAQUAA4ICDwAwggIKAoICAQC2iTZbB7cgNr2Cu+EWIAOVeq8O
37996 o1XJJZlKxdBWQYeQTSFgpBSHO839sj60ZwNq7eEPS8CRhXBF4EKe3ikj1AENoBB5uNsDvfOpL9HG
37997 4A/LnooUCri99lZi8cVytjIl2bLzvWXFDSxu1ZJvGIsAQRSCb0AgJnooD/Uefyf3lLE3PbfHkffi
37998 Aez9lInhzG7TNtYKGXmu1zSCZf98Qru23QumNK9LYP5/Q0kGi4xDuFby2X8hQxfqp0iVAXV16iul
37999 Q5XqFYSdCI0mblWbq9zSOdIxHWDirMxWRST1HFSr7obdljKF+ExP6JV2tgXdNiNnvP8V4so75qbs
38000 O+wmETRIjfaAKxojAuuKHDp2KntWFhxyKrOq42ClAJ8Em+JvHhRYW6Vsi1g8w7pOOlz34ZYrPu8H
38001 vKTlXcxNnw3h3Kq74W4a7I/htkxNeXJdFzULHdfBR9qWJODQcqhaX2YtENwvKhOuJv4KHBnM0D4L
38002 nMgJLvlblnpHnOl68wVQdJVznjAJ85eCXuaPOQgeWeU1FEIT/wCc976qUM/iUUjXuG+v+E5+M5iS
38003 FGI6dWPPe/regjupuznixL0sAA7IF6wT700ljtizkC+p2il9Ha90OrInwMEePnWjFqmveiJdnxMa
38004 z6eg6+OGCtP95paV1yPIN93EfKo2rJgaErHgTuixO/XWb/Ew1wIDAQABo0IwQDAPBgNVHRMBAf8E
38005 BTADAQH/MA4GA1UdDwEB/wQEAwIBBjAdBgNVHQ4EFgQUS8W0QGutHLOlHGVuRjaJhwUMDrYwDQYJ
38006 KoZIhvcNAQELBQADggIBAHNXPyzVlTJ+N9uWkusZXn5T50HsEbZH77Xe7XRcxfGOSeD8bpkTzZ+K
38007 2s06Ctg6Wgk/XzTQLwPSZh0avZyQN8gMjgdalEVGKua+etqhqaRpEpKwfTbURIfXUfEpY9Z1zRbk
38008 J4kd+MIySP3bmdCPX1R0zKxnNBFi2QwKN4fRoxdIjtIXHfbX/dtl6/2o1PXWT6RbdejF0mCy2wl+
38009 JYt7ulKSnj7oxXehPOBKc2thz4bcQ///If4jXSRK9dNtD2IEBVeC2m6kMyV5Sy5UGYvMLD0w6dEG
38010 /+gyRr61M3Z3qAFdlsHB1b6uJcDJHgoJIIihDsnzb02CVAAgp9KP5DlUFy6NHrgbuxu9mk47EDTc
38011 nIhT76IxW1hPkWLIwpqazRVdOKnWvvgTtZ8SafJQYqz7Fzf07rh1Z2AQ+4NQ+US1dZxAF7L+/Xld
38012 blhYXzD8AK6vM8EOTmy6p6ahfzLbOOCxchcKK5HsamMm7YnUeMx0HgX4a/6ManY5Ka5lIxKVCCIc
38013 l85bBu4M4ru8H0ST9tg4RQUh7eStqxK2A6RCLi3ECToDZ2mEmuFZkIoohdVddLHRDiBYmxOlsGOm
38014 7XtH/UVVMKTumtTm4ofvmMkyghEpIrwACjFeLQ/Ajulrso8uBtjRkcfGEvRM/TAXw8HaOFvjqerm
38015 obp573PYtlNXLfbQ4ddI
38016 -----END CERTIFICATE-----
38017
38018 Buypass Class 2 Root CA
38019 =======================
38020 -----BEGIN CERTIFICATE-----
38021 MIIFWTCCA0GgAwIBAgIBAjANBgkqhkiG9w0BAQsFADBOMQswCQYDVQQGEwJOTzEdMBsGA1UECgwU
38022 QnV5cGFzcyBBUy05ODMxNjMzMjcxIDAeBgNVBAMMF0J1eXBhc3MgQ2xhc3MgMiBSb290IENBMB4X
38023 DTEwMTAyNjA4MzgwM1oXDTQwMTAyNjA4MzgwM1owTjELMAkGA1UEBhMCTk8xHTAbBgNVBAoMFEJ1
38024 eXBhc3MgQVMtOTgzMTYzMzI3MSAwHgYDVQQDDBdCdXlwYXNzIENsYXNzIDIgUm9vdCBDQTCCAiIw
38025 DQYJKoZIhvcNAQEBBQADggIPADCCAgoCggIBANfHXvfBB9R3+0Mh9PT1aeTuMgHbo4Yf5FkNuud1
38026 g1Lr6hxhFUi7HQfKjK6w3Jad6sNgkoaCKHOcVgb/S2TwDCo3SbXlzwx87vFKu3MwZfPVL4O2fuPn
38027 9Z6rYPnT8Z2SdIrkHJasW4DptfQxh6NR/Md+oW+OU3fUl8FVM5I+GC911K2GScuVr1QGbNgGE41b
38028 /+EmGVnAJLqBcXmQRFBoJJRfuLMR8SlBYaNByyM21cHxMlAQTn/0hpPshNOOvEu/XAFOBz3cFIqU
38029 CqTqc/sLUegTBxj6DvEr0VQVfTzh97QZQmdiXnfgolXsttlpF9U6r0TtSsWe5HonfOV116rLJeff
38030 awrbD02TTqigzXsu8lkBarcNuAeBfos4GzjmCleZPe4h6KP1DBbdi+w0jpwqHAAVF41og9JwnxgI
38031 zRFo1clrUs3ERo/ctfPYV3Me6ZQ5BL/T3jjetFPsaRyifsSP5BtwrfKi+fv3FmRmaZ9JUaLiFRhn
38032 Bkp/1Wy1TbMz4GHrXb7pmA8y1x1LPC5aAVKRCfLf6o3YBkBjqhHk/sM3nhRSP/TizPJhk9H9Z2vX
38033 Uq6/aKtAQ6BXNVN48FP4YUIHZMbXb5tMOA1jrGKvNouicwoN9SG9dKpN6nIDSdvHXx1iY8f93ZHs
38034 M+71bbRuMGjeyNYmsHVee7QHIJihdjK4TWxPAgMBAAGjQjBAMA8GA1UdEwEB/wQFMAMBAf8wHQYD
38035 VR0OBBYEFMmAd+BikoL1RpzzuvdMw964o605MA4GA1UdDwEB/wQEAwIBBjANBgkqhkiG9w0BAQsF
38036 AAOCAgEAU18h9bqwOlI5LJKwbADJ784g7wbylp7ppHR/ehb8t/W2+xUbP6umwHJdELFx7rxP462s
38037 A20ucS6vxOOto70MEae0/0qyexAQH6dXQbLArvQsWdZHEIjzIVEpMMpghq9Gqx3tOluwlN5E40EI
38038 osHsHdb9T7bWR9AUC8rmyrV7d35BH16Dx7aMOZawP5aBQW9gkOLo+fsicdl9sz1Gv7SEr5AcD48S
38039 aq/v7h56rgJKihcrdv6sVIkkLE8/trKnToyokZf7KcZ7XC25y2a2t6hbElGFtQl+Ynhw/qlqYLYd
38040 DnkM/crqJIByw5c/8nerQyIKx+u2DISCLIBrQYoIwOula9+ZEsuK1V6ADJHgJgg2SMX6OBE1/yWD
38041 LfJ6v9r9jv6ly0UsH8SIU653DtmadsWOLB2jutXsMq7Aqqz30XpN69QH4kj3Io6wpJ9qzo6ysmD0
38042 oyLQI+uUWnpp3Q+/QFesa1lQ2aOZ4W7+jQF5JyMV3pKdewlNWudLSDBaGOYKbeaP4NK75t98biGC
38043 wWg5TbSYWGZizEqQXsP6JwSxeRV0mcy+rSDeJmAc61ZRpqPq5KM/p/9h3PFaTWwyI0PurKju7koS
38044 CTxdccK+efrCh2gdC/1cacwG0Jp9VJkqyTkaGa9LKkPzY11aWOIv4x3kqdbQCtCev9eBCfHJxyYN
38045 rJgWVqA=
38046 -----END CERTIFICATE-----
38047
38048 Buypass Class 3 Root CA
38049 =======================
38050 -----BEGIN CERTIFICATE-----
38051 MIIFWTCCA0GgAwIBAgIBAjANBgkqhkiG9w0BAQsFADBOMQswCQYDVQQGEwJOTzEdMBsGA1UECgwU
38052 QnV5cGFzcyBBUy05ODMxNjMzMjcxIDAeBgNVBAMMF0J1eXBhc3MgQ2xhc3MgMyBSb290IENBMB4X
38053 DTEwMTAyNjA4Mjg1OFoXDTQwMTAyNjA4Mjg1OFowTjELMAkGA1UEBhMCTk8xHTAbBgNVBAoMFEJ1
38054 eXBhc3MgQVMtOTgzMTYzMzI3MSAwHgYDVQQDDBdCdXlwYXNzIENsYXNzIDMgUm9vdCBDQTCCAiIw
38055 DQYJKoZIhvcNAQEBBQADggIPADCCAgoCggIBAKXaCpUWUOOV8l6ddjEGMnqb8RB2uACatVI2zSRH
38056 sJ8YZLya9vrVediQYkwiL944PdbgqOkcLNt4EemOaFEVcsfzM4fkoF0LXOBXByow9c3EN3coTRiR
38057 5r/VUv1xLXA+58bEiuPwKAv0dpihi4dVsjoT/Lc+JzeOIuOoTyrvYLs9tznDDgFHmV0ST9tD+leh
38058 7fmdvhFHJlsTmKtdFoqwNxxXnUX/iJY2v7vKB3tvh2PX0DJq1l1sDPGzbjniazEuOQAnFN44wOwZ
38059 ZoYS6J1yFhNkUsepNxz9gjDthBgd9K5c/3ATAOux9TN6S9ZV+AWNS2mw9bMoNlwUxFFzTWsL8TQH
38060 2xc519woe2v1n/MuwU8XKhDzzMro6/1rqy6any2CbgTUUgGTLT2G/H783+9CHaZr77kgxve9oKeV
38061 /afmiSTYzIw0bOIjL9kSGiG5VZFvC5F5GQytQIgLcOJ60g7YaEi7ghM5EFjp2CoHxhLbWNvSO1UQ
38062 RwUVZ2J+GGOmRj8JDlQyXr8NYnon74Do29lLBlo3WiXQCBJ31G8JUJc9yB3D34xFMFbG02SrZvPA
38063 Xpacw8Tvw3xrizp5f7NJzz3iiZ+gMEuFuZyUJHmPfWupRWgPK9Dx2hzLabjKSWJtyNBjYt1gD1iq
38064 j6G8BaVmos8bdrKEZLFMOVLAMLrwjEsCsLa3AgMBAAGjQjBAMA8GA1UdEwEB/wQFMAMBAf8wHQYD
38065 VR0OBBYEFEe4zf/lb+74suwvTg75JbCOPGvDMA4GA1UdDwEB/wQEAwIBBjANBgkqhkiG9w0BAQsF
38066 AAOCAgEAACAjQTUEkMJAYmDv4jVM1z+s4jSQuKFvdvoWFqRINyzpkMLyPPgKn9iB5btb2iUspKdV
38067 cSQy9sgL8rxq+JOssgfCX5/bzMiKqr5qb+FJEMwx14C7u8jYog5kV+qi9cKpMRXSIGrs/CIBKM+G
38068 uIAeqcwRpTzyFrNHnfzSgCHEy9BHcEGhyoMZCCxt8l13nIoUE9Q2HJLw5QY33KbmkJs4j1xrG0aG
38069 Q0JfPgEHU1RdZX33inOhmlRaHylDFCfChQ+1iHsaO5S3HWCntZznKWlXWpuTekMwGwPXYshApqr8
38070 ZORK15FTAaggiG6cX0S5y2CBNOxv033aSF/rtJC8LakcC6wc1aJoIIAE1vyxjy+7SjENSoYc6+I2
38071 KSb12tjE8nVhz36udmNKekBlk4f4HoCMhuWG1o8O/FMsYOgWYRqiPkN7zTlgVGr18okmAWiDSKIz
38072 6MkEkbIRNBE+6tBDGR8Dk5AM/1E9V/RBbuHLoL7ryWPNbczk+DaqaJ3tvV2XcEQNtg413OEMXbug
38073 UZTLfhbrES+jkkXITHHZvMmZUldGL1DPvTVp9D0VzgalLA8+9oG6lLvDu79leNKGef9JOxqDDPDe
38074 eOzI8k1MGt6CKfjBWtrt7uYnXuhF0J0cUahoq0Tj0Itq4/g7u9xN12TyUb7mqqta6THuBrxzvxNi
38075 Cp/HuZc=
38076 -----END CERTIFICATE-----
38077
38078 T-TeleSec GlobalRoot Class 3
38079 ============================
38080 -----BEGIN CERTIFICATE-----
38081 MIIDwzCCAqugAwIBAgIBATANBgkqhkiG9w0BAQsFADCBgjELMAkGA1UEBhMCREUxKzApBgNVBAoM
38082 IlQtU3lzdGVtcyBFbnRlcnByaXNlIFNlcnZpY2VzIEdtYkgxHzAdBgNVBAsMFlQtU3lzdGVtcyBU
38083 cnVzdCBDZW50ZXIxJTAjBgNVBAMMHFQtVGVsZVNlYyBHbG9iYWxSb290IENsYXNzIDMwHhcNMDgx
38084 MDAxMTAyOTU2WhcNMzMxMDAxMjM1OTU5WjCBgjELMAkGA1UEBhMCREUxKzApBgNVBAoMIlQtU3lz
38085 dGVtcyBFbnRlcnByaXNlIFNlcnZpY2VzIEdtYkgxHzAdBgNVBAsMFlQtU3lzdGVtcyBUcnVzdCBD
38086 ZW50ZXIxJTAjBgNVBAMMHFQtVGVsZVNlYyBHbG9iYWxSb290IENsYXNzIDMwggEiMA0GCSqGSIb3
38087 DQEBAQUAA4IBDwAwggEKAoIBAQC9dZPwYiJvJK7genasfb3ZJNW4t/zN8ELg63iIVl6bmlQdTQyK
38088 9tPPcPRStdiTBONGhnFBSivwKixVA9ZIw+A5OO3yXDw/RLyTPWGrTs0NvvAgJ1gORH8EGoel15YU
38089 NpDQSXuhdfsaa3Ox+M6pCSzyU9XDFES4hqX2iys52qMzVNn6chr3IhUciJFrf2blw2qAsCTz34ZF
38090 iP0Zf3WHHx+xGwpzJFu5ZeAsVMhg02YXP+HMVDNzkQI6pn97djmiH5a2OK61yJN0HZ65tOVgnS9W
38091 0eDrXltMEnAMbEQgqxHY9Bn20pxSN+f6tsIxO0rUFJmtxxr1XV/6B7h8DR/Wgx6zAgMBAAGjQjBA
38092 MA8GA1UdEwEB/wQFMAMBAf8wDgYDVR0PAQH/BAQDAgEGMB0GA1UdDgQWBBS1A/d2O2GCahKqGFPr
38093 AyGUv/7OyjANBgkqhkiG9w0BAQsFAAOCAQEAVj3vlNW92nOyWL6ukK2YJ5f+AbGwUgC4TeQbIXQb
38094 fsDuXmkqJa9c1h3a0nnJ85cp4IaH3gRZD/FZ1GSFS5mvJQQeyUapl96Cshtwn5z2r3Ex3XsFpSzT
38095 ucpH9sry9uetuUg/vBa3wW306gmv7PO15wWeph6KU1HWk4HMdJP2udqmJQV0eVp+QD6CSyYRMG7h
38096 P0HHRwA11fXT91Q+gT3aSWqas+8QPebrb9HIIkfLzM8BMZLZGOMivgkeGj5asuRrDFR6fUNOuIml
38097 e9eiPZaGzPImNC1qkp2aGtAw4l1OBLBfiyB+d8E9lYLRRpo7PHi4b6HQDWSieB4pTpPDpFQUWw==
38098 -----END CERTIFICATE-----
38099
38100 EE Certification Centre Root CA
38101 ===============================
38102 -----BEGIN CERTIFICATE-----
38103 MIIEAzCCAuugAwIBAgIQVID5oHPtPwBMyonY43HmSjANBgkqhkiG9w0BAQUFADB1MQswCQYDVQQG
38104 EwJFRTEiMCAGA1UECgwZQVMgU2VydGlmaXRzZWVyaW1pc2tlc2t1czEoMCYGA1UEAwwfRUUgQ2Vy
38105 dGlmaWNhdGlvbiBDZW50cmUgUm9vdCBDQTEYMBYGCSqGSIb3DQEJARYJcGtpQHNrLmVlMCIYDzIw
38106 MTAxMDMwMTAxMDMwWhgPMjAzMDEyMTcyMzU5NTlaMHUxCzAJBgNVBAYTAkVFMSIwIAYDVQQKDBlB
38107 UyBTZXJ0aWZpdHNlZXJpbWlza2Vza3VzMSgwJgYDVQQDDB9FRSBDZXJ0aWZpY2F0aW9uIENlbnRy
38108 ZSBSb290IENBMRgwFgYJKoZIhvcNAQkBFglwa2lAc2suZWUwggEiMA0GCSqGSIb3DQEBAQUAA4IB
38109 DwAwggEKAoIBAQDIIMDs4MVLqwd4lfNE7vsLDP90jmG7sWLqI9iroWUyeuuOF0+W2Ap7kaJjbMeM
38110 TC55v6kF/GlclY1i+blw7cNRfdCT5mzrMEvhvH2/UpvObntl8jixwKIy72KyaOBhU8E2lf/slLo2
38111 rpwcpzIP5Xy0xm90/XsY6KxX7QYgSzIwWFv9zajmofxwvI6Sc9uXp3whrj3B9UiHbCe9nyV0gVWw
38112 93X2PaRka9ZP585ArQ/dMtO8ihJTmMmJ+xAdTX7Nfh9WDSFwhfYggx/2uh8Ej+p3iDXE/+pOoYtN
38113 P2MbRMNE1CV2yreN1x5KZmTNXMWcg+HCCIia7E6j8T4cLNlsHaFLAgMBAAGjgYowgYcwDwYDVR0T
38114 AQH/BAUwAwEB/zAOBgNVHQ8BAf8EBAMCAQYwHQYDVR0OBBYEFBLyWj7qVhy/zQas8fElyalL1BSZ
38115 MEUGA1UdJQQ+MDwGCCsGAQUFBwMCBggrBgEFBQcDAQYIKwYBBQUHAwMGCCsGAQUFBwMEBggrBgEF
38116 BQcDCAYIKwYBBQUHAwkwDQYJKoZIhvcNAQEFBQADggEBAHv25MANqhlHt01Xo/6tu7Fq1Q+e2+Rj
38117 xY6hUFaTlrg4wCQiZrxTFGGVv9DHKpY5P30osxBAIWrEr7BSdxjhlthWXePdNl4dp1BUoMUq5KqM
38118 lIpPnTX/dqQGE5Gion0ARD9V04I8GtVbvFZMIi5GQ4okQC3zErg7cBqklrkar4dBGmoYDQZPxz5u
38119 uSlNDUmJEYcyW+ZLBMjkXOZ0c5RdFpgTlf7727FE5TpwrDdr5rMzcijJs1eg9gIWiAYLtqZLICjU
38120 3j2LrTcFU3T+bsy8QxdxXvnFzBqpYe73dgzzcvRyrc9yAjYHR8/vGVCJYMzpJJUPwssd8m92kMfM
38121 dcGWxZ0=
38122 -----END CERTIFICATE-----
38123 47961e7ef15667c93cd99be01b51f00a
38124 <?php
38125
38126 namespace Guzzle\Http\Exception;
38127
38128 class TooManyRedirectsException extends BadResponseException {}
38129 <?php
38130
38131 namespace Guzzle\Http\Exception;
38132
38133 /**
38134 * Exception when a client error is encountered (4xx codes)
38135 */
38136 class ClientErrorResponseException extends BadResponseException {}
38137 <?php
38138
38139 namespace Guzzle\Http\Exception;
38140
38141 use Guzzle\Http\Message\RequestInterface;
38142 use Guzzle\Http\Message\Response;
38143
38144 /**
38145 * Http request exception thrown when a bad response is received
38146 */
38147 class BadResponseException extends RequestException
38148 {
38149 /** @var Response */
38150 private $response;
38151
38152 /**
38153 * Factory method to create a new response exception based on the response code.
38154 *
38155 * @param RequestInterface $request Request
38156 * @param Response $response Response received
38157 *
38158 * @return BadResponseException
38159 */
38160 public static function factory(RequestInterface $request, Response $response)
38161 {
38162 if ($response->isClientError()) {
38163 $label = 'Client error response';
38164 $class = __NAMESPACE__ . '\\ClientErrorResponseException';
38165 } elseif ($response->isServerError()) {
38166 $label = 'Server error response';
38167 $class = __NAMESPACE__ . '\\ServerErrorResponseException';
38168 } else {
38169 $label = 'Unsuccessful response';
38170 $class = __CLASS__;
38171 $e = new self();
38172 }
38173
38174 $message = $label . PHP_EOL . implode(PHP_EOL, array(
38175 '[status code] ' . $response->getStatusCode(),
38176 '[reason phrase] ' . $response->getReasonPhrase(),
38177 '[url] ' . $request->getUrl(),
38178 ));
38179
38180 $e = new $class($message);
38181 $e->setResponse($response);
38182 $e->setRequest($request);
38183
38184 return $e;
38185 }
38186
38187 /**
38188 * Set the response that caused the exception
38189 *
38190 * @param Response $response Response to set
38191 */
38192 public function setResponse(Response $response)
38193 {
38194 $this->response = $response;
38195 }
38196
38197 /**
38198 * Get the response that caused the exception
38199 *
38200 * @return Response
38201 */
38202 public function getResponse()
38203 {
38204 return $this->response;
38205 }
38206 }
38207 <?php
38208
38209 namespace Guzzle\Http\Exception;
38210
38211 /**
38212 * Exception when a server error is encountered (5xx codes)
38213 */
38214 class ServerErrorResponseException extends BadResponseException {}
38215 <?php
38216
38217 namespace Guzzle\Http\Exception;
38218
38219 use Guzzle\Common\Exception\GuzzleException;
38220
38221 /**
38222 * Http exception interface
38223 */
38224 interface HttpException extends GuzzleException {}
38225 <?php
38226
38227 namespace Guzzle\Http\Exception;
38228
38229 use Guzzle\Common\Exception\RuntimeException;
38230 use Guzzle\Http\Message\RequestInterface;
38231
38232 /**
38233 * Http request exception
38234 */
38235 class RequestException extends RuntimeException implements HttpException
38236 {
38237 /** @var RequestInterface */
38238 protected $request;
38239
38240 /**
38241 * Set the request that caused the exception
38242 *
38243 * @param RequestInterface $request Request to set
38244 *
38245 * @return RequestException
38246 */
38247 public function setRequest(RequestInterface $request)
38248 {
38249 $this->request = $request;
38250
38251 return $this;
38252 }
38253
38254 /**
38255 * Get the request that caused the exception
38256 *
38257 * @return RequestInterface
38258 */
38259 public function getRequest()
38260 {
38261 return $this->request;
38262 }
38263 }
38264 <?php
38265
38266 namespace Guzzle\Http\Exception;
38267
38268 use Guzzle\Common\Exception\ExceptionCollection;
38269 use Guzzle\Http\Message\RequestInterface;
38270
38271 /**
38272 * Exception encountered during a multi transfer
38273 */
38274 class MultiTransferException extends ExceptionCollection
38275 {
38276 protected $successfulRequests = array();
38277 protected $failedRequests = array();
38278 protected $exceptionForRequest = array();
38279
38280 /**
38281 * Get all of the requests in the transfer
38282 *
38283 * @return array
38284 */
38285 public function getAllRequests()
38286 {
38287 return array_merge($this->successfulRequests, $this->failedRequests);
38288 }
38289
38290 /**
38291 * Add to the array of successful requests
38292 *
38293 * @param RequestInterface $request Successful request
38294 *
38295 * @return self
38296 */
38297 public function addSuccessfulRequest(RequestInterface $request)
38298 {
38299 $this->successfulRequests[] = $request;
38300
38301 return $this;
38302 }
38303
38304 /**
38305 * Add to the array of failed requests
38306 *
38307 * @param RequestInterface $request Failed request
38308 *
38309 * @return self
38310 */
38311 public function addFailedRequest(RequestInterface $request)
38312 {
38313 $this->failedRequests[] = $request;
38314
38315 return $this;
38316 }
38317
38318 /**
38319 * Add to the array of failed requests and associate with exceptions
38320 *
38321 * @param RequestInterface $request Failed request
38322 * @param \Exception $exception Exception to add and associate with
38323 *
38324 * @return self
38325 */
38326 public function addFailedRequestWithException(RequestInterface $request, \Exception $exception)
38327 {
38328 $this->add($exception)
38329 ->addFailedRequest($request)
38330 ->exceptionForRequest[spl_object_hash($request)] = $exception;
38331
38332 return $this;
38333 }
38334
38335 /**
38336 * Get the Exception that caused the given $request to fail
38337 *
38338 * @param RequestInterface $request Failed command
38339 *
38340 * @return \Exception|null
38341 */
38342 public function getExceptionForFailedRequest(RequestInterface $request)
38343 {
38344 $oid = spl_object_hash($request);
38345
38346 return isset($this->exceptionForRequest[$oid]) ? $this->exceptionForRequest[$oid] : null;
38347 }
38348
38349 /**
38350 * Set all of the successful requests
38351 *
38352 * @param array Array of requests
38353 *
38354 * @return self
38355 */
38356 public function setSuccessfulRequests(array $requests)
38357 {
38358 $this->successfulRequests = $requests;
38359
38360 return $this;
38361 }
38362
38363 /**
38364 * Set all of the failed requests
38365 *
38366 * @param array Array of requests
38367 *
38368 * @return self
38369 */
38370 public function setFailedRequests(array $requests)
38371 {
38372 $this->failedRequests = $requests;
38373
38374 return $this;
38375 }
38376
38377 /**
38378 * Get an array of successful requests sent in the multi transfer
38379 *
38380 * @return array
38381 */
38382 public function getSuccessfulRequests()
38383 {
38384 return $this->successfulRequests;
38385 }
38386
38387 /**
38388 * Get an array of failed requests sent in the multi transfer
38389 *
38390 * @return array
38391 */
38392 public function getFailedRequests()
38393 {
38394 return $this->failedRequests;
38395 }
38396
38397 /**
38398 * Check if the exception object contains a request
38399 *
38400 * @param RequestInterface $request Request to check
38401 *
38402 * @return bool
38403 */
38404 public function containsRequest(RequestInterface $request)
38405 {
38406 return in_array($request, $this->failedRequests, true) || in_array($request, $this->successfulRequests, true);
38407 }
38408 }
38409 <?php
38410
38411 namespace Guzzle\Http\Exception;
38412
38413 use Guzzle\Common\Exception\RuntimeException;
38414
38415 class CouldNotRewindStreamException extends RuntimeException implements HttpException {}
38416 <?php
38417
38418 namespace Guzzle\Http\Exception;
38419
38420 use Guzzle\Http\Curl\CurlHandle;
38421
38422 /**
38423 * cURL request exception
38424 */
38425 class CurlException extends RequestException
38426 {
38427 private $curlError;
38428 private $curlErrorNo;
38429 private $handle;
38430 private $curlInfo = array();
38431
38432 /**
38433 * Set the cURL error message
38434 *
38435 * @param string $error Curl error
38436 * @param int $number Curl error number
38437 *
38438 * @return self
38439 */
38440 public function setError($error, $number)
38441 {
38442 $this->curlError = $error;
38443 $this->curlErrorNo = $number;
38444
38445 return $this;
38446 }
38447
38448 /**
38449 * Set the associated curl handle
38450 *
38451 * @param CurlHandle $handle Curl handle
38452 *
38453 * @return self
38454 */
38455 public function setCurlHandle(CurlHandle $handle)
38456 {
38457 $this->handle = $handle;
38458
38459 return $this;
38460 }
38461
38462 /**
38463 * Get the associated cURL handle
38464 *
38465 * @return CurlHandle|null
38466 */
38467 public function getCurlHandle()
38468 {
38469 return $this->handle;
38470 }
38471
38472 /**
38473 * Get the associated cURL error message
38474 *
38475 * @return string|null
38476 */
38477 public function getError()
38478 {
38479 return $this->curlError;
38480 }
38481
38482 /**
38483 * Get the associated cURL error number
38484 *
38485 * @return int|null
38486 */
38487 public function getErrorNo()
38488 {
38489 return $this->curlErrorNo;
38490 }
38491
38492 /**
38493 * Returns curl information about the transfer
38494 *
38495 * @return array
38496 */
38497 public function getCurlInfo()
38498 {
38499 return $this->curlInfo;
38500 }
38501
38502 /**
38503 * Set curl transfer information
38504 *
38505 * @param array $info Array of curl transfer information
38506 *
38507 * @return self
38508 * @link http://php.net/manual/en/function.curl-getinfo.php
38509 */
38510 public function setCurlInfo(array $info)
38511 {
38512 $this->curlInfo = $info;
38513
38514 return $this;
38515 }
38516 }
38517 <?php
38518
38519 namespace Guzzle\Http;
38520
38521 use Guzzle\Common\Version;
38522 use Guzzle\Stream\Stream;
38523 use Guzzle\Common\Exception\InvalidArgumentException;
38524 use Guzzle\Http\Mimetypes;
38525
38526 /**
38527 * Entity body used with an HTTP request or response
38528 */
38529 class EntityBody extends Stream implements EntityBodyInterface
38530 {
38531 /** @var bool Content-Encoding of the entity body if known */
38532 protected $contentEncoding = false;
38533
38534 /** @var callable Method to invoke for rewinding a stream */
38535 protected $rewindFunction;
38536
38537 /**
38538 * Create a new EntityBody based on the input type
38539 *
38540 * @param resource|string|EntityBody $resource Entity body data
38541 * @param int $size Size of the data contained in the resource
38542 *
38543 * @return EntityBody
38544 * @throws InvalidArgumentException if the $resource arg is not a resource or string
38545 */
38546 public static function factory($resource = '', $size = null)
38547 {
38548 if ($resource instanceof EntityBodyInterface) {
38549 return $resource;
38550 }
38551
38552 switch (gettype($resource)) {
38553 case 'string':
38554 return self::fromString($resource);
38555 case 'resource':
38556 return new static($resource, $size);
38557 case 'object':
38558 if (method_exists($resource, '__toString')) {
38559 return self::fromString((string) $resource);
38560 }
38561 break;
38562 case 'array':
38563 return self::fromString(http_build_query($resource));
38564 }
38565
38566 throw new InvalidArgumentException('Invalid resource type');
38567 }
38568
38569 public function setRewindFunction($callable)
38570 {
38571 if (!is_callable($callable)) {
38572 throw new InvalidArgumentException('Must specify a callable');
38573 }
38574
38575 $this->rewindFunction = $callable;
38576
38577 return $this;
38578 }
38579
38580 public function rewind()
38581 {
38582 return $this->rewindFunction ? call_user_func($this->rewindFunction, $this) : parent::rewind();
38583 }
38584
38585 /**
38586 * Create a new EntityBody from a string
38587 *
38588 * @param string $string String of data
38589 *
38590 * @return EntityBody
38591 */
38592 public static function fromString($string)
38593 {
38594 $stream = fopen('php://temp', 'r+');
38595 if ($string !== '') {
38596 fwrite($stream, $string);
38597 rewind($stream);
38598 }
38599
38600 return new static($stream);
38601 }
38602
38603 public function compress($filter = 'zlib.deflate')
38604 {
38605 $result = $this->handleCompression($filter);
38606 $this->contentEncoding = $result ? $filter : false;
38607
38608 return $result;
38609 }
38610
38611 public function uncompress($filter = 'zlib.inflate')
38612 {
38613 $offsetStart = 0;
38614
38615 // When inflating gzipped data, the first 10 bytes must be stripped
38616 // if a gzip header is present
38617 if ($filter == 'zlib.inflate') {
38618 // @codeCoverageIgnoreStart
38619 if (!$this->isReadable() || ($this->isConsumed() && !$this->isSeekable())) {
38620 return false;
38621 }
38622 // @codeCoverageIgnoreEnd
38623 if (stream_get_contents($this->stream, 3, 0) === "\x1f\x8b\x08") {
38624 $offsetStart = 10;
38625 }
38626 }
38627
38628 $this->contentEncoding = false;
38629
38630 return $this->handleCompression($filter, $offsetStart);
38631 }
38632
38633 public function getContentLength()
38634 {
38635 return $this->getSize();
38636 }
38637
38638 public function getContentType()
38639 {
38640 return $this->getUri() ? Mimetypes::getInstance()->fromFilename($this->getUri()) : null;
38641 }
38642
38643 public function getContentMd5($rawOutput = false, $base64Encode = false)
38644 {
38645 if ($hash = self::getHash($this, 'md5', $rawOutput)) {
38646 return $hash && $base64Encode ? base64_encode($hash) : $hash;
38647 } else {
38648 return false;
38649 }
38650 }
38651
38652 /**
38653 * Calculate the MD5 hash of an entity body
38654 *
38655 * @param EntityBodyInterface $body Entity body to calculate the hash for
38656 * @param bool $rawOutput Whether or not to use raw output
38657 * @param bool $base64Encode Whether or not to base64 encode raw output (only if raw output is true)
38658 *
38659 * @return bool|string Returns an MD5 string on success or FALSE on failure
38660 * @deprecated This will be deprecated soon
38661 * @codeCoverageIgnore
38662 */
38663 public static function calculateMd5(EntityBodyInterface $body, $rawOutput = false, $base64Encode = false)
38664 {
38665 Version::warn(__CLASS__ . ' is deprecated. Use getContentMd5()');
38666 return $body->getContentMd5($rawOutput, $base64Encode);
38667 }
38668
38669 public function setStreamFilterContentEncoding($streamFilterContentEncoding)
38670 {
38671 $this->contentEncoding = $streamFilterContentEncoding;
38672
38673 return $this;
38674 }
38675
38676 public function getContentEncoding()
38677 {
38678 return strtr($this->contentEncoding, array(
38679 'zlib.deflate' => 'gzip',
38680 'bzip2.compress' => 'compress'
38681 )) ?: false;
38682 }
38683
38684 protected function handleCompression($filter, $offsetStart = 0)
38685 {
38686 // @codeCoverageIgnoreStart
38687 if (!$this->isReadable() || ($this->isConsumed() && !$this->isSeekable())) {
38688 return false;
38689 }
38690 // @codeCoverageIgnoreEnd
38691
38692 $handle = fopen('php://temp', 'r+');
38693 $filter = @stream_filter_append($handle, $filter, STREAM_FILTER_WRITE);
38694 if (!$filter) {
38695 return false;
38696 }
38697
38698 // Seek to the offset start if possible
38699 $this->seek($offsetStart);
38700 while ($data = fread($this->stream, 8096)) {
38701 fwrite($handle, $data);
38702 }
38703
38704 fclose($this->stream);
38705 $this->stream = $handle;
38706 stream_filter_remove($filter);
38707 $stat = fstat($this->stream);
38708 $this->size = $stat['size'];
38709 $this->rebuildCache();
38710 $this->seek(0);
38711
38712 // Remove any existing rewind function as the underlying stream has been replaced
38713 $this->rewindFunction = null;
38714
38715 return true;
38716 }
38717 }
38718 <?php
38719
38720 namespace Guzzle\Http;
38721
38722 use Guzzle\Stream\StreamInterface;
38723
38724 /**
38725 * Entity body used with an HTTP request or response
38726 */
38727 interface EntityBodyInterface extends StreamInterface
38728 {
38729 /**
38730 * Specify a custom callback used to rewind a non-seekable stream. This can be useful entity enclosing requests
38731 * that are redirected.
38732 *
38733 * @param mixed $callable Callable to invoke to rewind a non-seekable stream. The callback must accept an
38734 * EntityBodyInterface object, perform the rewind if possible, and return a boolean
38735 * representing whether or not the rewind was successful.
38736 * @return self
38737 */
38738 public function setRewindFunction($callable);
38739
38740 /**
38741 * If the stream is readable, compress the data in the stream using deflate compression. The uncompressed stream is
38742 * then closed, and the compressed stream then becomes the wrapped stream.
38743 *
38744 * @param string $filter Compression filter
38745 *
38746 * @return bool Returns TRUE on success or FALSE on failure
38747 */
38748 public function compress($filter = 'zlib.deflate');
38749
38750 /**
38751 * Decompress a deflated string. Once uncompressed, the uncompressed string is then used as the wrapped stream.
38752 *
38753 * @param string $filter De-compression filter
38754 *
38755 * @return bool Returns TRUE on success or FALSE on failure
38756 */
38757 public function uncompress($filter = 'zlib.inflate');
38758
38759 /**
38760 * Get the Content-Length of the entity body if possible (alias of getSize)
38761 *
38762 * @return int|bool Returns the Content-Length or false on failure
38763 */
38764 public function getContentLength();
38765
38766 /**
38767 * Guess the Content-Type of a local stream
38768 *
38769 * @return string|null
38770 * @see http://www.php.net/manual/en/function.finfo-open.php
38771 */
38772 public function getContentType();
38773
38774 /**
38775 * Get an MD5 checksum of the stream's contents
38776 *
38777 * @param bool $rawOutput Whether or not to use raw output
38778 * @param bool $base64Encode Whether or not to base64 encode raw output (only if raw output is true)
38779 *
38780 * @return bool|string Returns an MD5 string on success or FALSE on failure
38781 */
38782 public function getContentMd5($rawOutput = false, $base64Encode = false);
38783
38784 /**
38785 * Get the Content-Encoding of the EntityBody
38786 *
38787 * @return bool|string
38788 */
38789 public function getContentEncoding();
38790 }
38791 {
38792 "name": "guzzle/http",
38793 "description": "HTTP libraries used by Guzzle",
38794 "homepage": "http://guzzlephp.org/",
38795 "keywords": ["http client", "http", "client", "Guzzle", "curl"],
38796 "license": "MIT",
38797 "authors": [
38798 {
38799 "name": "Michael Dowling",
38800 "email": "mtdowling@gmail.com",
38801 "homepage": "https://github.com/mtdowling"
38802 }
38803 ],
38804 "require": {
38805 "php": ">=5.3.2",
38806 "guzzle/common": "self.version",
38807 "guzzle/parser": "self.version",
38808 "guzzle/stream": "self.version"
38809 },
38810 "suggest": {
38811 "ext-curl": "*"
38812 },
38813 "autoload": {
38814 "psr-0": { "Guzzle\\Http": "" }
38815 },
38816 "target-dir": "Guzzle/Http",
38817 "extra": {
38818 "branch-alias": {
38819 "dev-master": "3.7-dev"
38820 }
38821 }
38822 }
38823 <?php
38824
38825 namespace Guzzle\Http;
38826
38827 use Guzzle\Common\Event;
38828 use Guzzle\Common\HasDispatcherInterface;
38829 use Symfony\Component\EventDispatcher\EventDispatcher;
38830 use Symfony\Component\EventDispatcher\EventSubscriberInterface;
38831 use Symfony\Component\EventDispatcher\EventDispatcherInterface;
38832
38833 /**
38834 * EntityBody decorator that emits events for read and write methods
38835 */
38836 class IoEmittingEntityBody extends AbstractEntityBodyDecorator implements HasDispatcherInterface
38837 {
38838 /** @var EventDispatcherInterface */
38839 protected $eventDispatcher;
38840
38841 public static function getAllEvents()
38842 {
38843 return array('body.read', 'body.write');
38844 }
38845
38846 /**
38847 * {@inheritdoc}
38848 * @codeCoverageIgnore
38849 */
38850 public function setEventDispatcher(EventDispatcherInterface $eventDispatcher)
38851 {
38852 $this->eventDispatcher = $eventDispatcher;
38853
38854 return $this;
38855 }
38856
38857 public function getEventDispatcher()
38858 {
38859 if (!$this->eventDispatcher) {
38860 $this->eventDispatcher = new EventDispatcher();
38861 }
38862
38863 return $this->eventDispatcher;
38864 }
38865
38866 public function dispatch($eventName, array $context = array())
38867 {
38868 return $this->getEventDispatcher()->dispatch($eventName, new Event($context));
38869 }
38870
38871 /**
38872 * {@inheritdoc}
38873 * @codeCoverageIgnore
38874 */
38875 public function addSubscriber(EventSubscriberInterface $subscriber)
38876 {
38877 $this->getEventDispatcher()->addSubscriber($subscriber);
38878
38879 return $this;
38880 }
38881
38882 public function read($length)
38883 {
38884 $event = array(
38885 'body' => $this,
38886 'length' => $length,
38887 'read' => $this->body->read($length)
38888 );
38889 $this->dispatch('body.read', $event);
38890
38891 return $event['read'];
38892 }
38893
38894 public function write($string)
38895 {
38896 $event = array(
38897 'body' => $this,
38898 'write' => $string,
38899 'result' => $this->body->write($string)
38900 );
38901 $this->dispatch('body.write', $event);
38902
38903 return $event['result'];
38904 }
38905 }
38906 <?php
38907
38908 namespace Guzzle\Stream;
38909
38910 use Guzzle\Common\Exception\InvalidArgumentException;
38911
38912 /**
38913 * PHP stream implementation
38914 */
38915 class Stream implements StreamInterface
38916 {
38917 const STREAM_TYPE = 'stream_type';
38918 const WRAPPER_TYPE = 'wrapper_type';
38919 const IS_LOCAL = 'is_local';
38920 const IS_READABLE = 'is_readable';
38921 const IS_WRITABLE = 'is_writable';
38922 const SEEKABLE = 'seekable';
38923
38924 /** @var resource Stream resource */
38925 protected $stream;
38926
38927 /** @var int Size of the stream contents in bytes */
38928 protected $size;
38929
38930 /** @var array Stream cached data */
38931 protected $cache = array();
38932
38933 /** @var array Custom stream data */
38934 protected $customData = array();
38935
38936 /** @var array Hash table of readable and writeable stream types for fast lookups */
38937 protected static $readWriteHash = array(
38938 'read' => array(
38939 'r' => true, 'w+' => true, 'r+' => true, 'x+' => true, 'c+' => true,
38940 'rb' => true, 'w+b' => true, 'r+b' => true, 'x+b' => true, 'c+b' => true,
38941 'rt' => true, 'w+t' => true, 'r+t' => true, 'x+t' => true, 'c+t' => true, 'a+' => true
38942 ),
38943 'write' => array(
38944 'w' => true, 'w+' => true, 'rw' => true, 'r+' => true, 'x+' => true, 'c+' => true,
38945 'wb' => true, 'w+b' => true, 'r+b' => true, 'x+b' => true, 'c+b' => true,
38946 'w+t' => true, 'r+t' => true, 'x+t' => true, 'c+t' => true, 'a' => true, 'a+' => true
38947 )
38948 );
38949
38950 /**
38951 * @param resource $stream Stream resource to wrap
38952 * @param int $size Size of the stream in bytes. Only pass if the size cannot be obtained from the stream.
38953 *
38954 * @throws InvalidArgumentException if the stream is not a stream resource
38955 */
38956 public function __construct($stream, $size = null)
38957 {
38958 $this->setStream($stream, $size);
38959 }
38960
38961 /**
38962 * Closes the stream when the helper is destructed
38963 */
38964 public function __destruct()
38965 {
38966 $this->close();
38967 }
38968
38969 public function __toString()
38970 {
38971 if (!$this->isReadable() || (!$this->isSeekable() && $this->isConsumed())) {
38972 return '';
38973 }
38974
38975 $originalPos = $this->ftell();
38976 $body = stream_get_contents($this->stream, -1, 0);
38977 $this->seek($originalPos);
38978
38979 return $body;
38980 }
38981
38982 public function close()
38983 {
38984 if (is_resource($this->stream)) {
38985 fclose($this->stream);
38986 }
38987 $this->cache[self::IS_READABLE] = false;
38988 $this->cache[self::IS_WRITABLE] = false;
38989 }
38990
38991 /**
38992 * Calculate a hash of a Stream
38993 *
38994 * @param StreamInterface $stream Stream to calculate the hash for
38995 * @param string $algo Hash algorithm (e.g. md5, crc32, etc)
38996 * @param bool $rawOutput Whether or not to use raw output
38997 *
38998 * @return bool|string Returns false on failure or a hash string on success
38999 */
39000 public static function getHash(StreamInterface $stream, $algo, $rawOutput = false)
39001 {
39002 $pos = $stream->ftell();
39003 if (!$stream->seek(0)) {
39004 return false;
39005 }
39006
39007 $ctx = hash_init($algo);
39008 while ($data = $stream->read(8192)) {
39009 hash_update($ctx, $data);
39010 }
39011
39012 $out = hash_final($ctx, (bool) $rawOutput);
39013 $stream->seek($pos);
39014
39015 return $out;
39016 }
39017
39018 public function getMetaData($key = null)
39019 {
39020 $meta = stream_get_meta_data($this->stream);
39021
39022 return !$key ? $meta : (array_key_exists($key, $meta) ? $meta[$key] : null);
39023 }
39024
39025 public function getStream()
39026 {
39027 return $this->stream;
39028 }
39029
39030 public function setStream($stream, $size = null)
39031 {
39032 if (!is_resource($stream)) {
39033 throw new InvalidArgumentException('Stream must be a resource');
39034 }
39035
39036 $this->size = $size;
39037 $this->stream = $stream;
39038 $this->rebuildCache();
39039
39040 return $this;
39041 }
39042
39043 public function detachStream()
39044 {
39045 $this->stream = null;
39046
39047 return $this;
39048 }
39049
39050 public function getWrapper()
39051 {
39052 return $this->cache[self::WRAPPER_TYPE];
39053 }
39054
39055 public function getWrapperData()
39056 {
39057 return $this->getMetaData('wrapper_data') ?: array();
39058 }
39059
39060 public function getStreamType()
39061 {
39062 return $this->cache[self::STREAM_TYPE];
39063 }
39064
39065 public function getUri()
39066 {
39067 return $this->cache['uri'];
39068 }
39069
39070 public function getSize()
39071 {
39072 if ($this->size !== null) {
39073 return $this->size;
39074 }
39075
39076 // If the stream is a file based stream and local, then use fstat
39077 clearstatcache(true, $this->cache['uri']);
39078 $stats = fstat($this->stream);
39079 if (isset($stats['size'])) {
39080 $this->size = $stats['size'];
39081 return $this->size;
39082 } elseif ($this->cache[self::IS_READABLE] && $this->cache[self::SEEKABLE]) {
39083 // Only get the size based on the content if the the stream is readable and seekable
39084 $pos = $this->ftell();
39085 $this->size = strlen((string) $this);
39086 $this->seek($pos);
39087 return $this->size;
39088 }
39089
39090 return false;
39091 }
39092
39093 public function isReadable()
39094 {
39095 return $this->cache[self::IS_READABLE];
39096 }
39097
39098 public function isRepeatable()
39099 {
39100 return $this->cache[self::IS_READABLE] && $this->cache[self::SEEKABLE];
39101 }
39102
39103 public function isWritable()
39104 {
39105 return $this->cache[self::IS_WRITABLE];
39106 }
39107
39108 public function isConsumed()
39109 {
39110 return feof($this->stream);
39111 }
39112
39113 public function feof()
39114 {
39115 return $this->isConsumed();
39116 }
39117
39118 public function isLocal()
39119 {
39120 return $this->cache[self::IS_LOCAL];
39121 }
39122
39123 public function isSeekable()
39124 {
39125 return $this->cache[self::SEEKABLE];
39126 }
39127
39128 public function setSize($size)
39129 {
39130 $this->size = $size;
39131
39132 return $this;
39133 }
39134
39135 public function seek($offset, $whence = SEEK_SET)
39136 {
39137 return $this->cache[self::SEEKABLE] ? fseek($this->stream, $offset, $whence) === 0 : false;
39138 }
39139
39140 public function read($length)
39141 {
39142 return fread($this->stream, $length);
39143 }
39144
39145 public function write($string)
39146 {
39147 // We can't know the size after writing anything
39148 $this->size = null;
39149
39150 return fwrite($this->stream, $string);
39151 }
39152
39153 public function ftell()
39154 {
39155 return ftell($this->stream);
39156 }
39157
39158 public function rewind()
39159 {
39160 return $this->seek(0);
39161 }
39162
39163 public function readLine($maxLength = null)
39164 {
39165 if (!$this->cache[self::IS_READABLE]) {
39166 return false;
39167 } else {
39168 return $maxLength ? fgets($this->getStream(), $maxLength) : fgets($this->getStream());
39169 }
39170 }
39171
39172 public function setCustomData($key, $value)
39173 {
39174 $this->customData[$key] = $value;
39175
39176 return $this;
39177 }
39178
39179 public function getCustomData($key)
39180 {
39181 return isset($this->customData[$key]) ? $this->customData[$key] : null;
39182 }
39183
39184 /**
39185 * Reprocess stream metadata
39186 */
39187 protected function rebuildCache()
39188 {
39189 $this->cache = stream_get_meta_data($this->stream);
39190 $this->cache[self::IS_LOCAL] = stream_is_local($this->stream);
39191 $this->cache[self::IS_READABLE] = isset(self::$readWriteHash['read'][$this->cache['mode']]);
39192 $this->cache[self::IS_WRITABLE] = isset(self::$readWriteHash['write'][$this->cache['mode']]);
39193 }
39194 }
39195 <?php
39196
39197 namespace Guzzle\Stream;
39198
39199 /**
39200 * OO interface to PHP streams
39201 */
39202 interface StreamInterface
39203 {
39204 /**
39205 * Convert the stream to a string if the stream is readable and the stream is seekable.
39206 *
39207 * @return string
39208 */
39209 public function __toString();
39210
39211 /**
39212 * Close the underlying stream
39213 */
39214 public function close();
39215
39216 /**
39217 * Get stream metadata
39218 *
39219 * @param string $key Specific metadata to retrieve
39220 *
39221 * @return array|mixed|null
39222 */
39223 public function getMetaData($key = null);
39224
39225 /**
39226 * Get the stream resource
39227 *
39228 * @return resource
39229 */
39230 public function getStream();
39231
39232 /**
39233 * Set the stream that is wrapped by the object
39234 *
39235 * @param resource $stream Stream resource to wrap
39236 * @param int $size Size of the stream in bytes. Only pass if the size cannot be obtained from the stream.
39237 *
39238 * @return self
39239 */
39240 public function setStream($stream, $size = null);
39241
39242 /**
39243 * Detach the current stream resource
39244 *
39245 * @return self
39246 */
39247 public function detachStream();
39248
39249 /**
39250 * Get the stream wrapper type
39251 *
39252 * @return string
39253 */
39254 public function getWrapper();
39255
39256 /**
39257 * Wrapper specific data attached to this stream.
39258 *
39259 * @return array
39260 */
39261 public function getWrapperData();
39262
39263 /**
39264 * Get a label describing the underlying implementation of the stream
39265 *
39266 * @return string
39267 */
39268 public function getStreamType();
39269
39270 /**
39271 * Get the URI/filename associated with this stream
39272 *
39273 * @return string
39274 */
39275 public function getUri();
39276
39277 /**
39278 * Get the size of the stream if able
39279 *
39280 * @return int|bool
39281 */
39282 public function getSize();
39283
39284 /**
39285 * Check if the stream is readable
39286 *
39287 * @return bool
39288 */
39289 public function isReadable();
39290
39291 /**
39292 * Check if the stream is repeatable
39293 *
39294 * @return bool
39295 */
39296 public function isRepeatable();
39297
39298 /**
39299 * Check if the stream is writable
39300 *
39301 * @return bool
39302 */
39303 public function isWritable();
39304
39305 /**
39306 * Check if the stream has been consumed
39307 *
39308 * @return bool
39309 */
39310 public function isConsumed();
39311
39312 /**
39313 * Alias of isConsumed
39314 *
39315 * @return bool
39316 */
39317 public function feof();
39318
39319 /**
39320 * Check if the stream is a local stream vs a remote stream
39321 *
39322 * @return bool
39323 */
39324 public function isLocal();
39325
39326 /**
39327 * Check if the string is repeatable
39328 *
39329 * @return bool
39330 */
39331 public function isSeekable();
39332
39333 /**
39334 * Specify the size of the stream in bytes
39335 *
39336 * @param int $size Size of the stream contents in bytes
39337 *
39338 * @return self
39339 */
39340 public function setSize($size);
39341
39342 /**
39343 * Seek to a position in the stream
39344 *
39345 * @param int $offset Stream offset
39346 * @param int $whence Where the offset is applied
39347 *
39348 * @return bool Returns TRUE on success or FALSE on failure
39349 * @link http://www.php.net/manual/en/function.fseek.php
39350 */
39351 public function seek($offset, $whence = SEEK_SET);
39352
39353 /**
39354 * Read data from the stream
39355 *
39356 * @param int $length Up to length number of bytes read.
39357 *
39358 * @return string|bool Returns the data read from the stream or FALSE on failure or EOF
39359 */
39360 public function read($length);
39361
39362 /**
39363 * Write data to the stream
39364 *
39365 * @param string $string The string that is to be written.
39366 *
39367 * @return int|bool Returns the number of bytes written to the stream on success or FALSE on failure.
39368 */
39369 public function write($string);
39370
39371 /**
39372 * Returns the current position of the file read/write pointer
39373 *
39374 * @return int|bool Returns the position of the file pointer or false on error
39375 */
39376 public function ftell();
39377
39378 /**
39379 * Rewind to the beginning of the stream
39380 *
39381 * @return bool Returns true on success or false on failure
39382 */
39383 public function rewind();
39384
39385 /**
39386 * Read a line from the stream up to the maximum allowed buffer length
39387 *
39388 * @param int $maxLength Maximum buffer length
39389 *
39390 * @return string|bool
39391 */
39392 public function readLine($maxLength = null);
39393
39394 /**
39395 * Set custom data on the stream
39396 *
39397 * @param string $key Key to set
39398 * @param mixed $value Value to set
39399 *
39400 * @return self
39401 */
39402 public function setCustomData($key, $value);
39403
39404 /**
39405 * Get custom data from the stream
39406 *
39407 * @param string $key Key to retrieve
39408 *
39409 * @return null|mixed
39410 */
39411 public function getCustomData($key);
39412 }
39413 {
39414 "name": "guzzle/stream",
39415 "description": "Guzzle stream wrapper component",
39416 "homepage": "http://guzzlephp.org/",
39417 "keywords": ["stream", "component", "guzzle"],
39418 "license": "MIT",
39419 "authors": [
39420 {
39421 "name": "Michael Dowling",
39422 "email": "mtdowling@gmail.com",
39423 "homepage": "https://github.com/mtdowling"
39424 }
39425 ],
39426 "require": {
39427 "php": ">=5.3.2",
39428 "guzzle/common": "self.version"
39429 },
39430 "suggest": {
39431 "guzzle/http": "To convert Guzzle request objects to PHP streams"
39432 },
39433 "autoload": {
39434 "psr-0": { "Guzzle\\Stream": "" }
39435 },
39436 "target-dir": "Guzzle/Stream",
39437 "extra": {
39438 "branch-alias": {
39439 "dev-master": "3.7-dev"
39440 }
39441 }
39442 }
39443 <?php
39444
39445 namespace Guzzle\Stream;
39446
39447 use Guzzle\Http\Message\RequestInterface;
39448
39449 /**
39450 * Interface used for creating streams from requests
39451 */
39452 interface StreamRequestFactoryInterface
39453 {
39454 /**
39455 * Create a stream based on a request object
39456 *
39457 * @param RequestInterface $request Base the stream on a request
39458 * @param array|resource $context A stream_context_options resource or array of parameters used to create a
39459 * stream context.
39460 * @param array $params Optional array of parameters specific to the factory
39461 *
39462 * @return StreamInterface Returns a stream object
39463 * @throws \Guzzle\Common\Exception\RuntimeException if the stream cannot be opened or an error occurs
39464 */
39465 public function fromRequest(RequestInterface $request, $context = array(), array $params = array());
39466 }
39467 <?php
39468
39469 namespace Guzzle\Stream;
39470
39471 use Guzzle\Common\Exception\InvalidArgumentException;
39472 use Guzzle\Common\Exception\RuntimeException;
39473 use Guzzle\Http\Message\EntityEnclosingRequestInterface;
39474 use Guzzle\Http\Message\RequestInterface;
39475 use Guzzle\Http\Url;
39476
39477 /**
39478 * Factory used to create fopen streams using PHP's http and https stream wrappers
39479 *
39480 * Note: PHP's http stream wrapper only supports streaming downloads. It does not support streaming uploads.
39481 */
39482 class PhpStreamRequestFactory implements StreamRequestFactoryInterface
39483 {
39484 /** @var resource Stream context options */
39485 protected $context;
39486
39487 /** @var array Stream context */
39488 protected $contextOptions;
39489
39490 /** @var Url Stream URL */
39491 protected $url;
39492
39493 /** @var array Last response headers received by the HTTP request */
39494 protected $lastResponseHeaders;
39495
39496 /**
39497 * {@inheritdoc}
39498 *
39499 * The $params array can contain the following custom keys specific to the PhpStreamRequestFactory:
39500 * - stream_class: The name of a class to create instead of a Guzzle\Stream\Stream object
39501 */
39502 public function fromRequest(RequestInterface $request, $context = array(), array $params = array())
39503 {
39504 if (is_resource($context)) {
39505 $this->contextOptions = stream_context_get_options($context);
39506 $this->context = $context;
39507 } elseif (is_array($context) || !$context) {
39508 $this->contextOptions = $context;
39509 $this->createContext($params);
39510 } elseif ($context) {
39511 throw new InvalidArgumentException('$context must be an array or resource');
39512 }
39513
39514 // Dispatch the before send event
39515 $request->dispatch('request.before_send', array(
39516 'request' => $request,
39517 'context' => $this->context,
39518 'context_options' => $this->contextOptions
39519 ));
39520
39521 $this->setUrl($request);
39522 $this->addDefaultContextOptions($request);
39523 $this->addSslOptions($request);
39524 $this->addBodyOptions($request);
39525 $this->addProxyOptions($request);
39526
39527 // Create the file handle but silence errors
39528 return $this->createStream($params)
39529 ->setCustomData('request', $request)
39530 ->setCustomData('response_headers', $this->getLastResponseHeaders());
39531 }
39532
39533 /**
39534 * Set an option on the context and the internal options array
39535 *
39536 * @param string $wrapper Stream wrapper name of http
39537 * @param string $name Context name
39538 * @param mixed $value Context value
39539 * @param bool $overwrite Set to true to overwrite an existing value
39540 */
39541 protected function setContextValue($wrapper, $name, $value, $overwrite = false)
39542 {
39543 if (!isset($this->contextOptions[$wrapper])) {
39544 $this->contextOptions[$wrapper] = array($name => $value);
39545 } elseif (!$overwrite && isset($this->contextOptions[$wrapper][$name])) {
39546 return;
39547 }
39548 $this->contextOptions[$wrapper][$name] = $value;
39549 stream_context_set_option($this->context, $wrapper, $name, $value);
39550 }
39551
39552 /**
39553 * Create a stream context
39554 *
39555 * @param array $params Parameter array
39556 */
39557 protected function createContext(array $params)
39558 {
39559 $options = $this->contextOptions;
39560 $this->context = $this->createResource(function () use ($params, $options) {
39561 return stream_context_create($options, $params);
39562 });
39563 }
39564
39565 /**
39566 * Get the last response headers received by the HTTP request
39567 *
39568 * @return array
39569 */
39570 public function getLastResponseHeaders()
39571 {
39572 return $this->lastResponseHeaders;
39573 }
39574
39575 /**
39576 * Adds the default context options to the stream context options
39577 *
39578 * @param RequestInterface $request Request
39579 */
39580 protected function addDefaultContextOptions(RequestInterface $request)
39581 {
39582 $this->setContextValue('http', 'method', $request->getMethod());
39583 $this->setContextValue('http', 'header', $request->getHeaderLines());
39584 // Force 1.0 for now until PHP fully support chunked transfer-encoding decoding
39585 $this->setContextValue('http', 'protocol_version', '1.0');
39586 $this->setContextValue('http', 'ignore_errors', true);
39587 }
39588
39589 /**
39590 * Set the URL to use with the factory
39591 *
39592 * @param RequestInterface $request Request that owns the URL
39593 */
39594 protected function setUrl(RequestInterface $request)
39595 {
39596 $this->url = $request->getUrl(true);
39597
39598 // Check for basic Auth username
39599 if ($request->getUsername()) {
39600 $this->url->setUsername($request->getUsername());
39601 }
39602
39603 // Check for basic Auth password
39604 if ($request->getPassword()) {
39605 $this->url->setPassword($request->getPassword());
39606 }
39607 }
39608
39609 /**
39610 * Add SSL options to the stream context
39611 *
39612 * @param RequestInterface $request Request
39613 */
39614 protected function addSslOptions(RequestInterface $request)
39615 {
39616 if ($verify = $request->getCurlOptions()->get(CURLOPT_SSL_VERIFYPEER)) {
39617 $this->setContextValue('ssl', 'verify_peer', true, true);
39618 if ($cafile = $request->getCurlOptions()->get(CURLOPT_CAINFO)) {
39619 $this->setContextValue('ssl', 'cafile', $cafile, true);
39620 }
39621 } else {
39622 $this->setContextValue('ssl', 'verify_peer', false, true);
39623 }
39624 }
39625
39626 /**
39627 * Add body (content) specific options to the context options
39628 *
39629 * @param RequestInterface $request
39630 */
39631 protected function addBodyOptions(RequestInterface $request)
39632 {
39633 // Add the content for the request if needed
39634 if (!($request instanceof EntityEnclosingRequestInterface)) {
39635 return;
39636 }
39637
39638 if (count($request->getPostFields())) {
39639 $this->setContextValue('http', 'content', (string) $request->getPostFields(), true);
39640 } elseif ($request->getBody()) {
39641 $this->setContextValue('http', 'content', (string) $request->getBody(), true);
39642 }
39643
39644 // Always ensure a content-length header is sent
39645 if (isset($this->contextOptions['http']['content'])) {
39646 $headers = isset($this->contextOptions['http']['header']) ? $this->contextOptions['http']['header'] : array();
39647 $headers[] = 'Content-Length: ' . strlen($this->contextOptions['http']['content']);
39648 $this->setContextValue('http', 'header', $headers, true);
39649 }
39650 }
39651
39652 /**
39653 * Add proxy parameters to the context if needed
39654 *
39655 * @param RequestInterface $request Request
39656 */
39657 protected function addProxyOptions(RequestInterface $request)
39658 {
39659 if ($proxy = $request->getCurlOptions()->get(CURLOPT_PROXY)) {
39660 $this->setContextValue('http', 'proxy', $proxy);
39661 }
39662 }
39663
39664 /**
39665 * Create the stream for the request with the context options
39666 *
39667 * @param array $params Parameters of the stream
39668 *
39669 * @return StreamInterface
39670 */
39671 protected function createStream(array $params)
39672 {
39673 $http_response_header = null;
39674 $url = $this->url;
39675 $context = $this->context;
39676 $fp = $this->createResource(function () use ($context, $url, &$http_response_header) {
39677 return fopen((string) $url, 'r', false, $context);
39678 });
39679
39680 // Determine the class to instantiate
39681 $className = isset($params['stream_class']) ? $params['stream_class'] : __NAMESPACE__ . '\\Stream';
39682
39683 /** @var $stream StreamInterface */
39684 $stream = new $className($fp);
39685
39686 // Track the response headers of the request
39687 if (isset($http_response_header)) {
39688 $this->lastResponseHeaders = $http_response_header;
39689 $this->processResponseHeaders($stream);
39690 }
39691
39692 return $stream;
39693 }
39694
39695 /**
39696 * Process response headers
39697 *
39698 * @param StreamInterface $stream
39699 */
39700 protected function processResponseHeaders(StreamInterface $stream)
39701 {
39702 // Set the size on the stream if it was returned in the response
39703 foreach ($this->lastResponseHeaders as $header) {
39704 if (($pos = stripos($header, 'Content-Length:')) === 0) {
39705 $stream->setSize(trim(substr($header, 15)));
39706 }
39707 }
39708 }
39709
39710 /**
39711 * Create a resource and check to ensure it was created successfully
39712 *
39713 * @param callable $callback Closure to invoke that must return a valid resource
39714 *
39715 * @return resource
39716 * @throws RuntimeException on error
39717 */
39718 protected function createResource($callback)
39719 {
39720 // Turn off error reporting while we try to initiate the request
39721 $level = error_reporting(0);
39722 $resource = call_user_func($callback);
39723 error_reporting($level);
39724
39725 // If the resource could not be created, then grab the last error and throw an exception
39726 if (false === $resource) {
39727 $message = 'Error creating resource. ';
39728 foreach (error_get_last() as $key => $value) {
39729 $message .= "[{$key}] {$value} ";
39730 }
39731 throw new RuntimeException(trim($message));
39732 }
39733
39734 return $resource;
39735 }
39736 }
39737 <?php
39738
39739 namespace Guzzle\Inflection;
39740
39741 /**
39742 * Default inflection implementation
39743 */
39744 class Inflector implements InflectorInterface
39745 {
39746 /** @var InflectorInterface */
39747 protected static $default;
39748
39749 /**
39750 * Get the default inflector object that has support for caching
39751 *
39752 * @return MemoizingInflector
39753 */
39754 public static function getDefault()
39755 {
39756 // @codeCoverageIgnoreStart
39757 if (!self::$default) {
39758 self::$default = new MemoizingInflector(new self());
39759 }
39760 // @codeCoverageIgnoreEnd
39761
39762 return self::$default;
39763 }
39764
39765 public function snake($word)
39766 {
39767 return ctype_lower($word) ? $word : strtolower(preg_replace('/(.)([A-Z])/', "$1_$2", $word));
39768 }
39769
39770 public function camel($word)
39771 {
39772 return str_replace(' ', '', ucwords(strtr($word, '_-', ' ')));
39773 }
39774 }
39775 <?php
39776
39777 namespace Guzzle\Inflection;
39778
39779 /**
39780 * Decorator used to add pre-computed inflection mappings to an inflector
39781 */
39782 class PreComputedInflector implements InflectorInterface
39783 {
39784 /** @var array Array of pre-computed inflections */
39785 protected $mapping = array(
39786 'snake' => array(),
39787 'camel' => array()
39788 );
39789
39790 /** @var InflectorInterface Decorated inflector */
39791 protected $decoratedInflector;
39792
39793 /**
39794 * @param InflectorInterface $inflector Inflector being decorated
39795 * @param array $snake Hash of pre-computed camel to snake
39796 * @param array $camel Hash of pre-computed snake to camel
39797 * @param bool $mirror Mirror snake and camel reflections
39798 */
39799 public function __construct(InflectorInterface $inflector, array $snake = array(), array $camel = array(), $mirror = false)
39800 {
39801 if ($mirror) {
39802 $camel = array_merge(array_flip($snake), $camel);
39803 $snake = array_merge(array_flip($camel), $snake);
39804 }
39805
39806 $this->decoratedInflector = $inflector;
39807 $this->mapping = array(
39808 'snake' => $snake,
39809 'camel' => $camel
39810 );
39811 }
39812
39813 public function snake($word)
39814 {
39815 return isset($this->mapping['snake'][$word])
39816 ? $this->mapping['snake'][$word]
39817 : $this->decoratedInflector->snake($word);
39818 }
39819
39820 /**
39821 * Converts strings from snake_case to upper CamelCase
39822 *
39823 * @param string $word Value to convert into upper CamelCase
39824 *
39825 * @return string
39826 */
39827 public function camel($word)
39828 {
39829 return isset($this->mapping['camel'][$word])
39830 ? $this->mapping['camel'][$word]
39831 : $this->decoratedInflector->camel($word);
39832 }
39833 }
39834 <?php
39835
39836 namespace Guzzle\Inflection;
39837
39838 /**
39839 * Inflector interface used to convert the casing of words
39840 */
39841 interface InflectorInterface
39842 {
39843 /**
39844 * Converts strings from camel case to snake case (e.g. CamelCase camel_case).
39845 *
39846 * @param string $word Word to convert to snake case
39847 *
39848 * @return string
39849 */
39850 public function snake($word);
39851
39852 /**
39853 * Converts strings from snake_case to upper CamelCase
39854 *
39855 * @param string $word Value to convert into upper CamelCase
39856 *
39857 * @return string
39858 */
39859 public function camel($word);
39860 }
39861 <?php
39862
39863 namespace Guzzle\Inflection;
39864
39865 /**
39866 * Decorator used to add memoization to previously inflected words
39867 */
39868 class MemoizingInflector implements InflectorInterface
39869 {
39870 /** @var array Array of cached inflections */
39871 protected $cache = array(
39872 'snake' => array(),
39873 'camel' => array()
39874 );
39875
39876 /** @var int Max entries per cache */
39877 protected $maxCacheSize;
39878
39879 /** @var InflectorInterface Decorated inflector */
39880 protected $decoratedInflector;
39881
39882 /**
39883 * @param InflectorInterface $inflector Inflector being decorated
39884 * @param int $maxCacheSize Maximum number of cached items to hold per cache
39885 */
39886 public function __construct(InflectorInterface $inflector, $maxCacheSize = 500)
39887 {
39888 $this->decoratedInflector = $inflector;
39889 $this->maxCacheSize = $maxCacheSize;
39890 }
39891
39892 public function snake($word)
39893 {
39894 if (!isset($this->cache['snake'][$word])) {
39895 $this->pruneCache('snake');
39896 $this->cache['snake'][$word] = $this->decoratedInflector->snake($word);
39897 }
39898
39899 return $this->cache['snake'][$word];
39900 }
39901
39902 /**
39903 * Converts strings from snake_case to upper CamelCase
39904 *
39905 * @param string $word Value to convert into upper CamelCase
39906 *
39907 * @return string
39908 */
39909 public function camel($word)
39910 {
39911 if (!isset($this->cache['camel'][$word])) {
39912 $this->pruneCache('camel');
39913 $this->cache['camel'][$word] = $this->decoratedInflector->camel($word);
39914 }
39915
39916 return $this->cache['camel'][$word];
39917 }
39918
39919 /**
39920 * Prune one of the named caches by removing 20% of the cache if it is full
39921 *
39922 * @param string $cache Type of cache to prune
39923 */
39924 protected function pruneCache($cache)
39925 {
39926 if (count($this->cache[$cache]) == $this->maxCacheSize) {
39927 $this->cache[$cache] = array_slice($this->cache[$cache], $this->maxCacheSize * 0.2);
39928 }
39929 }
39930 }
39931 {
39932 "name": "guzzle/inflection",
39933 "description": "Guzzle inflection component",
39934 "homepage": "http://guzzlephp.org/",
39935 "keywords": ["inflection", "guzzle"],
39936 "license": "MIT",
39937 "authors": [
39938 {
39939 "name": "Michael Dowling",
39940 "email": "mtdowling@gmail.com",
39941 "homepage": "https://github.com/mtdowling"
39942 }
39943 ],
39944 "require": {
39945 "php": ">=5.3.2"
39946 },
39947 "autoload": {
39948 "psr-0": { "Guzzle\\Inflection": "" }
39949 },
39950 "target-dir": "Guzzle/Inflection",
39951 "extra": {
39952 "branch-alias": {
39953 "dev-master": "3.7-dev"
39954 }
39955 }
39956 }
39957 <?xml version="1.0" encoding="UTF-8"?>
39958 <project basedir="../../" default="deploy">
39959
39960 <property name="git.status" value=""/>
39961 <property name="git.currentbranch" value=""/>
39962 <target name="check-git-branch-status">
39963 <exec command="git status -s -b" outputProperty="git.currentbranch" />
39964 <if>
39965 <equals arg1="${git.currentbranch}" arg2="## ${head}" trim="true"/>
39966 <then>
39967 <echo>On branch ${head}</echo>
39968 </then>
39969 <else>
39970 <fail message="-Dhead=${head} arg did not match ${git.currentbranch}"/>
39971 </else>
39972 </if>
39973 <exec command="git status -s" outputProperty="git.status" />
39974 <if>
39975 <equals arg1="${git.status}" arg2="" trim="true"/>
39976 <then>
39977 <echo>working directory clean</echo>
39978 </then>
39979 <else>
39980 <echo>${git.status}</echo>
39981 <fail message="Working directory isn't clean." />
39982 </else>
39983 </if>
39984 </target>
39985
39986 <property name="version.changelog" value=""/>
39987 <property name="version.version" value=""/>
39988 <target name="check-changelog-version">
39989 <exec executable="fgrep" outputProperty="version.changelog">
39990 <arg value="${new.version} ("/>
39991 <arg value="${project.basedir}/CHANGELOG.md"/>
39992 </exec>
39993 <if>
39994 <equals arg1="${version.changelog}" arg2="" trim="true"/>
39995 <then>
39996 <fail message="${new.version} not mentioned in CHANGELOG"/>
39997 </then>
39998 </if>
39999
40000 <exec executable="fgrep" outputProperty="version.version">
40001 <arg value="const VERSION = '${new.version}'"/>
40002 <arg value="${project.basedir}/src/Guzzle/Common/Version.php"/>
40003 </exec>
40004 <if>
40005 <equals arg1="${version.version}" arg2="" trim="true"/>
40006 <then>
40007 <fail message="${new.version} not mentioned in Guzzle\Common\Version"/>
40008 </then>
40009 </if>
40010
40011 <echo>ChangeLog Match: ${version.changelog}</echo>
40012 <echo>Guzzle\Common\Version Match: ${version.version}</echo>
40013 </target>
40014
40015 <target name="help" description="HELP AND REMINDERS about what you can do with this project">
40016 <echo>releasing: phing -Dnew.version=3.0.x -Dhead=master release</echo>
40017 <echo>--</echo>
40018 <exec command="phing -l" passthru="true"/>
40019 </target>
40020
40021 <target name="release" depends="check-changelog-version,check-git-branch-status"
40022 description="tag, subtree split, package, deploy: Use: phing -Dnew.version=[TAG] -Dhead=[BRANCH] release">
40023 <if>
40024 <isset property="new.version" />
40025 <then>
40026 <if>
40027 <contains string="${new.version}" substring="v" casesensitive="false" />
40028 <then>
40029 <fail message="Please specify version as [0-9].[0-9].[0-9]. (I'll add v for you.)"/>
40030 </then>
40031 <else>
40032
40033 <echo>BEGINNING RELEASE FOR ${new.version}</echo>
40034
40035 <!-- checkout the specified branch -->
40036 <!-- <gitcheckout repository="${repo.dir}" branchname="${head}" gitPath="${cmd.git}" /> -->
40037 <!-- Ensure that the tag exists -->
40038 <!-- push the tag up so subsplit will get it -->
40039 <!--gitpush repository="${repo.dir}" tags="true" gitPath="${cmd.git}" /-->
40040
40041 <!-- now do the subsplits -->
40042 <guzzlesubsplit
40043 repository="${repo.dir}"
40044 remote="${guzzle.remote}"
40045 heads="${head}"
40046 tags="v${new.version}"
40047 base="src"
40048 subIndicatorFile="composer.json"
40049 gitPath="${cmd.git}" />
40050
40051 <!-- Copy .md files into the PEAR package -->
40052 <copy file="${repo.dir}/LICENSE" tofile=".subsplit/src/Guzzle/LICENSE.md" />
40053 <copy file="${repo.dir}/README.md" tofile=".subsplit/src/Guzzle/README.md" />
40054 <copy file="${repo.dir}/CHANGELOG.md" tofile=".subsplit/src/Guzzle/CHANGELOG.md" />
40055
40056 <!-- and now the pear packages -->
40057 <guzzlepear
40058 version="${new.version}"
40059 makephar="true"
40060 />
40061 </else>
40062
40063 </if>
40064 </then>
40065
40066 <else>
40067 <echo>Tip: to create a new release, do: phing -Dnew.version=[TAG] -Dhead=[BRANCH] release</echo>
40068 </else>
40069
40070 </if>
40071 </target>
40072
40073 <target name="peartest">
40074 <guzzlepear
40075 version="${new.version}"
40076 deploy="false"
40077 makephar="false"
40078 />
40079 </target>
40080
40081 <target name="package-phar" depends="test-init" description="Create a phar with an autoloader">
40082 <pharpackage
40083 destfile="${dir.output}/guzzle.phar"
40084 basedir="${project.basedir}/.subsplit"
40085 stub="phar-stub.php"
40086 signature="md5">
40087 <fileset dir="${project.basedir}/.subsplit">
40088 <include name="src/**/*.php" />
40089 <include name="src/**/*.pem" />
40090 <include name="src/**/*.md5" />
40091 <include name="vendor/symfony/class-loader/Symfony/Component/ClassLoader/UniversalClassLoader.php" />
40092 <include name="vendor/symfony/event-dispatcher/**/*.php" />
40093 <include name="vendor/doctrine/common/lib/Doctrine/Common/Cache/*.php" />
40094 <include name="vendor/monolog/monolog/src/**/*.php" />
40095 </fileset>
40096 <metadata>
40097 <element name="author" value="Michael Dowling" />
40098 </metadata>
40099 </pharpackage>
40100 <exec command="php -d guzzle_phar=${dir.output}/guzzle.phar `which phpunit`" passthru="true" />
40101 </target>
40102
40103 </project>
40104 <?xml version="1.0" encoding="UTF-8"?>
40105 <project basedir="../../" default="metrics">
40106
40107 <target name="metrics" depends="phploc,pdepend"/>
40108
40109 <target name="phploc" description="Measure project size using PHPLOC">
40110 <exec command="phploc --log-csv ${dir.output}/logs/phploc.csv ${project.basedir}/src" passthru="true"/>
40111 </target>
40112
40113 <target name="pdepend" description="Calculate software metrics using PHP_Depend">
40114 <mkdir dir="${dir.output}/pdepend" />
40115 <phpdepend>
40116 <fileset dir="${project.basedir}/src">
40117 <include name="**/*.php" />
40118 </fileset>
40119 <logger type="jdepend-xml" outfile="${dir.output}/logs/jdepend.xml" />
40120 <logger type="jdepend-chart" outfile="${dir.output}/pdepend/dependencies.svg" />
40121 <logger type="overview-pyramid" outfile="${dir.output}/pdepend/overview-pyramid.svg" />
40122 <analyzer type="coderank-mode" value="method" />
40123 </phpdepend>
40124 </target>
40125
40126 </project>
40127 <?xml version="1.0" encoding="UTF-8"?>
40128 <project basedir="../../" default="install-dependencies">
40129
40130 <property name="cmd.composer" value="" />
40131 <property name="cmd.git" value="" />
40132 <property name="cmd.testserver" value="" />
40133
40134 <!--
40135 Our custom tasks
40136 -->
40137 <taskdef name="composerlint" classname="phing.tasks.ComposerLintTask" />
40138 <taskdef name="testserver" classname="phing.tasks.NodeServerTask" />
40139 <taskdef name="guzzlesubsplit" classname="phing.tasks.GuzzleSubSplitTask" />
40140 <taskdef name="guzzlepear" classname="phing.tasks.GuzzlePearPharPackageTask" />
40141
40142 <!--
40143 Handle locating composer (global) or composer.phar, and
40144 if necessary, installing composer.phar locally.
40145 -->
40146 <target name="install-composer" description="Installs composer locally if it can't be found in build.properties or global install.">
40147 <if>
40148 <contains string="${cmd.composer}" substring="composer" />
40149 <then>
40150 <echo>Using composer at ${cmd.composer}</echo>
40151 </then>
40152 <else>
40153 <exec command="which composer" outputProperty="cmd.composer" />
40154 <if>
40155 <contains string="${cmd.composer}" substring="composer" />
40156 <then>
40157 <echo>Using composer at ${cmd.composer}</echo>
40158 </then>
40159
40160 <elseif>
40161 <available file="${project.basedir}/composer.phar" />
40162 <then>
40163 <echo>Composer is installed locally</echo>
40164 <property name="cmd.composer" value="${php.interpreter} ${project.basedir}/composer.phar" override="true"/>
40165 </then>
40166 </elseif>
40167
40168 <else>
40169 <echo message="Installing composer locally" />
40170 <exec command="curl -s http://getcomposer.org/installer | php" passthru="true" />
40171 <property name="cmd.composer" value="${php.interpreter} ${project.basedir}/composer.phar" override="true"/>
40172 </else>
40173 </if>
40174 </else>
40175 </if>
40176 <echo message="cmd.composer is ${cmd.composer}"/>
40177 </target>
40178
40179 <target name="find-git">
40180 <if>
40181 <contains string="${cmd.git}" substring="git" />
40182 <then>
40183 <echo>using git at ${cmd.git}</echo>
40184 </then>
40185 <else>
40186 <exec command="which git" outputProperty="cmd.git" />
40187 <echo>found git at ${cmd.git}</echo>
40188 </else>
40189 </if>
40190 </target>
40191
40192 <target name="install-dependencies" depends="install-composer">
40193 <exec command="${cmd.composer} install --dev" passthru="true" />
40194 </target>
40195
40196 <target name="update-dependencies" depends="install-composer">
40197 <exec command="${cmd.composer} update --dev" passthru="true" />
40198 </target>
40199
40200 <target name="clean-dependencies">
40201 <delete dir="${project.basedir}/vendor"/>
40202 <delete file="${project.basedir}/composer.lock" />
40203 </target>
40204
40205 </project>
40206 <?xml version="1.0" encoding="UTF-8"?>
40207 <project basedir="../../" default="preflight">
40208
40209 <!-- instead of phingcall sub-tasks, use an empty target with deps -->
40210 <target name="preflight" depends="test-init,lint"/>
40211
40212 <target name="travisci" depends="install-dependencies,composer-lint,php-lint,test"/>
40213
40214 <target name="lint" depends="install-composer,composer-lint,php-lint" description="lint-check PHP and JSON" />
40215
40216 <target name="composer-lint" description="lint-check composer.json only">
40217 <composerlint dir="${project.basedir}/src" file="{$project.basedir}/composer.json" />
40218 </target>
40219
40220 <target name="php-lint" description="lint-check just the PHP source">
40221 <phplint level="info">
40222 <fileset dir="${project.basedir}/src">
40223 <include name="**/*.php" />
40224 </fileset>
40225 </phplint>
40226 <phplint level="info">
40227 <fileset dir="${project.basedir}/tests">
40228 <include name="**/*.php" />
40229 </fileset>
40230 </phplint>
40231 </target>
40232
40233 <target name="sniff" description="Check code for PSR-2 compliance">
40234 <phpcodesniffer
40235 standard="PSR2"
40236 format="summary" />
40237
40238 </target>
40239 </project>
40240 # update this if you're working on a fork.
40241 guzzle.remote=git@github.com:guzzle/guzzle.git
40242
40243 # github credentials -- only used by GitHub API calls to create subtree repos
40244 github.basicauth=username:password
40245 # for the subtree split and testing
40246 github.org=guzzle
40247
40248 # your git path
40249 cmd.git=/usr/bin/git
40250
40251 # your composer command
40252 cmd.composer=composer
40253
40254 # test server start
40255 cmd.testserver="~/.nvm/nvm.sh run v0.6.14"# you may need to update this if you're working on a fork.
40256 guzzle.remote=git@github.com:guzzle/guzzle.git
40257
40258 # github credentials -- only used by GitHub API calls to create subtree repos
40259 github.basicauth=username:password
40260 # for the subtree split and testing
40261 github.org=guzzle
40262
40263 # your git path
40264 cmd.git=git
40265
40266 # your composer command
40267 cmd.composer=composer
40268
40269 # test server start
40270 cmd.testserver=node<?php
40271 /**
40272 * This file is part of Guzzle's build process.
40273 *
40274 * @copyright 2012 Clay Loveless <clay@php.net>
40275 * @license http://claylo.mit-license.org/2012/ MIT License
40276 */
40277
40278 require_once 'phing/Task.php';
40279 require_once 'PEAR/PackageFileManager2.php';
40280 require_once 'PEAR/PackageFileManager/File.php';
40281 require_once 'PEAR/Packager.php';
40282
40283 class GuzzlePearPharPackageTask extends Task
40284 {
40285 private $dir;
40286 private $version;
40287 private $deploy = true;
40288 private $makephar = true;
40289
40290 private $subpackages = array();
40291
40292 public function setVersion($str)
40293 {
40294 $this->version = $str;
40295 }
40296
40297 public function getVersion()
40298 {
40299 return $this->version;
40300 }
40301
40302 public function setDeploy($deploy)
40303 {
40304 $this->deploy = (bool) $deploy;
40305 }
40306
40307 public function getDeploy()
40308 {
40309 return $this->deploy;
40310 }
40311
40312 public function setMakephar($makephar)
40313 {
40314 $this->makephar = (bool) $makephar;
40315 }
40316
40317 public function getMakephar()
40318 {
40319 return $this->makephar;
40320 }
40321
40322 private $basedir;
40323 private $guzzleinfo;
40324 private $changelog_release_date;
40325 private $changelog_notes = '-';
40326
40327 public function main()
40328 {
40329 $this->basedir = $this->getProject()->getBasedir();
40330
40331 if (!is_dir((string) $this->basedir.'/.subsplit')) {
40332 throw new BuildException('PEAR packaging requires .subsplit directory');
40333 }
40334
40335 // main composer file
40336 $composer_file = file_get_contents((string) $this->basedir.'/.subsplit/composer.json');
40337 $this->guzzleinfo = json_decode($composer_file, true);
40338
40339 // make sure we have a target
40340 $pearwork = (string) $this->basedir . '/build/pearwork';
40341 if (!is_dir($pearwork)) {
40342 mkdir($pearwork, 0777, true);
40343 }
40344 $pearlogs = (string) $this->basedir . '/build/artifacts/logs';
40345 if (!is_dir($pearlogs)) {
40346 mkdir($pearlogs, 0777, true);
40347 }
40348
40349 $version = $this->getVersion();
40350 $this->grabChangelog();
40351 if ($version[0] == '2') {
40352 $this->log('building single PEAR package');
40353 $this->buildSinglePackage();
40354 } else {
40355 // $this->log("building PEAR subpackages");
40356 // $this->createSubPackages();
40357 // $this->log("building PEAR bundle package");
40358 $this->buildSinglePackage();
40359 }
40360
40361 if ($this->getMakephar()) {
40362 $this->log("building PHAR");
40363 $this->getProject()->executeTarget('package-phar');
40364 }
40365
40366 if ($this->getDeploy()) {
40367 $this->doDeployment();
40368 }
40369 }
40370
40371 public function doDeployment()
40372 {
40373 $basedir = (string) $this->basedir;
40374 $this->log('beginning PEAR/PHAR deployment');
40375
40376 chdir($basedir . '/build/pearwork');
40377 if (is_dir($basedir . '/build/pearwork/guzzle.github.com')) {
40378 exec('rm -rf guzzle.github.com');
40379 }
40380 passthru('git clone git@github.com:guzzle/guzzle.github.com');
40381
40382 // add PEAR packages
40383 foreach (scandir($basedir . '/build/pearwork') as $file) {
40384 if (substr($file, -4) == '.tgz') {
40385 passthru('pirum add guzzle.github.com/pear '.$file);
40386 }
40387 }
40388
40389 // if we have a new phar, add it
40390 if ($this->getMakephar() && file_exists($basedir.'/build/artifacts/guzzle.phar')) {
40391 rename($basedir.'/build/artifacts/guzzle.phar', $basedir.'/build/pearwork/guzzle.github.com/guzzle.phar');
40392 }
40393
40394 // add and commit
40395 chdir($basedir . '/build/pearwork/guzzle.github.com');
40396 passthru('git add --all .');
40397 passthru('git commit -m "Pushing PEAR/PHAR release for '.$this->getVersion().'" && git push');
40398 }
40399
40400 public function buildSinglePackage()
40401 {
40402 $v = $this->getVersion();
40403 $apiversion = $v[0] . '.0.0';
40404
40405 $opts = array(
40406 'packagedirectory' => (string) $this->basedir . '/.subsplit/src/',
40407 'filelistgenerator' => 'file',
40408 'ignore' => array('*composer.json'),
40409 'baseinstalldir' => '/',
40410 'packagefile' => 'package.xml'
40411 //'outputdirectory' => (string) $this->basedir . '/build/pearwork/'
40412 );
40413 $pfm = new PEAR_PackageFileManager2();
40414 $e = $pfm->setOptions($opts);
40415 $pfm->addRole('md', 'doc');
40416 $pfm->addRole('pem', 'php');
40417 $pfm->setPackage('Guzzle');
40418 $pfm->setSummary("Object-oriented PHP HTTP Client for PHP 5.3+");
40419 $pfm->setDescription($this->guzzleinfo['description']);
40420 $pfm->setPackageType('php');
40421 $pfm->setChannel('guzzlephp.org/pear');
40422 $pfm->setAPIVersion($apiversion);
40423 $pfm->setReleaseVersion($this->getVersion());
40424 $pfm->setAPIStability('stable');
40425 $pfm->setReleaseStability('stable');
40426 $pfm->setNotes($this->changelog_notes);
40427 $pfm->setPackageType('php');
40428 $pfm->setLicense('MIT', 'http://github.com/guzzle/guzzle/blob/master/LICENSE');
40429 $pfm->addMaintainer('lead', 'mtdowling', 'Michael Dowling', 'mtdowling@gmail.com', 'yes');
40430 $pfm->setDate($this->changelog_release_date);
40431 $pfm->generateContents();
40432
40433 $phpdep = $this->guzzleinfo['require']['php'];
40434 $phpdep = str_replace('>=', '', $phpdep);
40435 $pfm->setPhpDep($phpdep);
40436 $pfm->addExtensionDep('required', 'curl');
40437 $pfm->setPearinstallerDep('1.4.6');
40438 $pfm->addPackageDepWithChannel('required', 'EventDispatcher', 'pear.symfony.com', '2.1.0');
40439 if (!empty($this->subpackages)) {
40440 foreach ($this->subpackages as $package) {
40441 $pkg = dirname($package);
40442 $pkg = str_replace('/', '_', $pkg);
40443 $pfm->addConflictingPackageDepWithChannel($pkg, 'guzzlephp.org/pear', false, $apiversion);
40444 }
40445 }
40446
40447 ob_start();
40448 $startdir = getcwd();
40449 chdir((string) $this->basedir . '/build/pearwork');
40450
40451 echo "DEBUGGING GENERATED PACKAGE FILE\n";
40452 $result = $pfm->debugPackageFile();
40453 if ($result) {
40454 $out = $pfm->writePackageFile();
40455 echo "\n\n\nWRITE PACKAGE FILE RESULT:\n";
40456 var_dump($out);
40457 // load up package file and build package
40458 $packager = new PEAR_Packager();
40459 echo "\n\n\nBUILDING PACKAGE FROM PACKAGE FILE:\n";
40460 $dest_package = $packager->package($opts['packagedirectory'].'package.xml');
40461 var_dump($dest_package);
40462 } else {
40463 echo "\n\n\nDEBUGGING RESULT:\n";
40464 var_dump($result);
40465 }
40466 echo "removing package.xml";
40467 unlink($opts['packagedirectory'].'package.xml');
40468 $log = ob_get_clean();
40469 file_put_contents((string) $this->basedir . '/build/artifacts/logs/pear_package.log', $log);
40470 chdir($startdir);
40471 }
40472
40473 public function createSubPackages()
40474 {
40475 $version = $this->getVersion();
40476 $this->findComponents();
40477
40478 foreach ($this->subpackages as $package) {
40479 $baseinstalldir = dirname($package);
40480 $dir = (string) $this->basedir.'/.subsplit/src/' . $baseinstalldir;
40481 $composer_file = file_get_contents((string) $this->basedir.'/.subsplit/src/'. $package);
40482 $package_info = json_decode($composer_file, true);
40483 $this->log('building ' . $package_info['target-dir'] . ' subpackage');
40484 $this->buildSubPackage($dir, $baseinstalldir, $package_info);
40485 }
40486 }
40487
40488 public function buildSubPackage($dir, $baseinstalldir, $info)
40489 {
40490 $package = str_replace('/', '_', $baseinstalldir);
40491 $opts = array(
40492 'packagedirectory' => $dir,
40493 'filelistgenerator' => 'file',
40494 'ignore' => array('*composer.json', '*package.xml'),
40495 'baseinstalldir' => '/' . $info['target-dir'],
40496 'packagefile' => 'package.xml'
40497 );
40498 $pfm = new PEAR_PackageFileManager2();
40499 $e = $pfm->setOptions($opts);
40500 $pfm->setPackage($package);
40501 $pfm->setSummary($info['description']);
40502 $pfm->setDescription($info['description']);
40503 $pfm->setPackageType('php');
40504 $pfm->setChannel('guzzlephp.org/pear');
40505 $pfm->setAPIVersion('3.0.0');
40506 $pfm->setReleaseVersion($this->getVersion());
40507 $pfm->setAPIStability('stable');
40508 $pfm->setReleaseStability('stable');
40509 $pfm->setNotes($this->changelog_notes);
40510 $pfm->setPackageType('php');
40511 $pfm->setLicense('MIT', 'http://github.com/guzzle/guzzle/blob/master/LICENSE');
40512 $pfm->addMaintainer('lead', 'mtdowling', 'Michael Dowling', 'mtdowling@gmail.com', 'yes');
40513 $pfm->setDate($this->changelog_release_date);
40514 $pfm->generateContents();
40515
40516 $phpdep = $this->guzzleinfo['require']['php'];
40517 $phpdep = str_replace('>=', '', $phpdep);
40518 $pfm->setPhpDep($phpdep);
40519 $pfm->setPearinstallerDep('1.4.6');
40520
40521 foreach ($info['require'] as $type => $version) {
40522 if ($type == 'php') {
40523 continue;
40524 }
40525 if ($type == 'symfony/event-dispatcher') {
40526 $pfm->addPackageDepWithChannel('required', 'EventDispatcher', 'pear.symfony.com', '2.1.0');
40527 }
40528 if ($type == 'ext-curl') {
40529 $pfm->addExtensionDep('required', 'curl');
40530 }
40531 if (substr($type, 0, 6) == 'guzzle') {
40532 $gdep = str_replace('/', ' ', $type);
40533 $gdep = ucwords($gdep);
40534 $gdep = str_replace(' ', '_', $gdep);
40535 $pfm->addPackageDepWithChannel('required', $gdep, 'guzzlephp.org/pear', $this->getVersion());
40536 }
40537 }
40538
40539 // can't have main Guzzle package AND sub-packages
40540 $pfm->addConflictingPackageDepWithChannel('Guzzle', 'guzzlephp.org/pear', false, $apiversion);
40541
40542 ob_start();
40543 $startdir = getcwd();
40544 chdir((string) $this->basedir . '/build/pearwork');
40545
40546 echo "DEBUGGING GENERATED PACKAGE FILE\n";
40547 $result = $pfm->debugPackageFile();
40548 if ($result) {
40549 $out = $pfm->writePackageFile();
40550 echo "\n\n\nWRITE PACKAGE FILE RESULT:\n";
40551 var_dump($out);
40552 // load up package file and build package
40553 $packager = new PEAR_Packager();
40554 echo "\n\n\nBUILDING PACKAGE FROM PACKAGE FILE:\n";
40555 $dest_package = $packager->package($opts['packagedirectory'].'/package.xml');
40556 var_dump($dest_package);
40557 } else {
40558 echo "\n\n\nDEBUGGING RESULT:\n";
40559 var_dump($result);
40560 }
40561 echo "removing package.xml";
40562 unlink($opts['packagedirectory'].'/package.xml');
40563 $log = ob_get_clean();
40564 file_put_contents((string) $this->basedir . '/build/artifacts/logs/pear_package_'.$package.'.log', $log);
40565 chdir($startdir);
40566 }
40567
40568 public function findComponents()
40569 {
40570 $ds = new DirectoryScanner();
40571 $ds->setBasedir((string) $this->basedir.'/.subsplit/src');
40572 $ds->setIncludes(array('**/composer.json'));
40573 $ds->scan();
40574 $files = $ds->getIncludedFiles();
40575 $this->subpackages = $files;
40576 }
40577
40578 public function grabChangelog()
40579 {
40580 $cl = file((string) $this->basedir.'/.subsplit/CHANGELOG.md');
40581 $notes = '';
40582 $in_version = false;
40583 $release_date = null;
40584
40585 foreach ($cl as $line) {
40586 $line = trim($line);
40587 if (preg_match('/^\* '.$this->getVersion().' \(([0-9\-]+)\)$/', $line, $matches)) {
40588 $release_date = $matches[1];
40589 $in_version = true;
40590 continue;
40591 }
40592 if ($in_version && empty($line) && empty($notes)) {
40593 continue;
40594 }
40595 if ($in_version && ! empty($line)) {
40596 $notes .= $line."\n";
40597 }
40598 if ($in_version && empty($line) && !empty($notes)) {
40599 $in_version = false;
40600 }
40601 }
40602 $this->changelog_release_date = $release_date;
40603
40604 if (! empty($notes)) {
40605 $this->changelog_notes = $notes;
40606 }
40607 }
40608 }
40609 <?php
40610 /**
40611 * Phing task for composer validation.
40612 *
40613 * @copyright 2012 Clay Loveless <clay@php.net>
40614 * @license http://claylo.mit-license.org/2012/ MIT License
40615 */
40616
40617 require_once 'phing/Task.php';
40618
40619 class ComposerLintTask extends Task
40620 {
40621 protected $dir = null;
40622 protected $file = null;
40623 protected $passthru = false;
40624 protected $composer = null;
40625
40626 /**
40627 * The setter for the dir
40628 *
40629 * @param string $str Directory to crawl recursively for composer files
40630 */
40631 public function setDir($str)
40632 {
40633 $this->dir = $str;
40634 }
40635
40636 /**
40637 * The setter for the file
40638 *
40639 * @param string $str Individual file to validate
40640 */
40641 public function setFile($str)
40642 {
40643 $this->file = $str;
40644 }
40645
40646 /**
40647 * Whether to use PHP's passthru() function instead of exec()
40648 *
40649 * @param boolean $passthru If passthru shall be used
40650 */
40651 public function setPassthru($passthru)
40652 {
40653 $this->passthru = (bool) $passthru;
40654 }
40655
40656 /**
40657 * Composer to execute. If unset, will attempt composer.phar in project
40658 * basedir, and if that fails, will attempt global composer
40659 * installation.
40660 *
40661 * @param string $str Individual file to validate
40662 */
40663 public function setComposer($str)
40664 {
40665 $this->file = $str;
40666 }
40667
40668 /**
40669 * The init method: do init steps
40670 */
40671 public function init()
40672 {
40673 // nothing needed here
40674 }
40675
40676 /**
40677 * The main entry point
40678 */
40679 public function main()
40680 {
40681 if ($this->composer === null) {
40682 $this->findComposer();
40683 }
40684
40685 $files = array();
40686 if (!empty($this->file) && file_exists($this->file)) {
40687 $files[] = $this->file;
40688 }
40689
40690 if (!empty($this->dir)) {
40691 $found = $this->findFiles();
40692 foreach ($found as $file) {
40693 $files[] = $this->dir . DIRECTORY_SEPARATOR . $file;
40694 }
40695 }
40696
40697 foreach ($files as $file) {
40698
40699 $cmd = $this->composer . ' validate ' . $file;
40700 $cmd = escapeshellcmd($cmd);
40701
40702 if ($this->passthru) {
40703 $retval = null;
40704 passthru($cmd, $retval);
40705 if ($retval == 1) {
40706 throw new BuildException('invalid composer.json');
40707 }
40708 } else {
40709 $out = array();
40710 $retval = null;
40711 exec($cmd, $out, $retval);
40712 if ($retval == 1) {
40713 $err = join("\n", $out);
40714 throw new BuildException($err);
40715 } else {
40716 $this->log($out[0]);
40717 }
40718 }
40719
40720 }
40721
40722 }
40723
40724 /**
40725 * Find the composer.json files using Phing's directory scanner
40726 *
40727 * @return array
40728 */
40729 protected function findFiles()
40730 {
40731 $ds = new DirectoryScanner();
40732 $ds->setBasedir($this->dir);
40733 $ds->setIncludes(array('**/composer.json'));
40734 $ds->scan();
40735 return $ds->getIncludedFiles();
40736 }
40737
40738 /**
40739 * Find composer installation
40740 *
40741 */
40742 protected function findComposer()
40743 {
40744 $basedir = $this->project->getBasedir();
40745 $php = $this->project->getProperty('php.interpreter');
40746
40747 if (file_exists($basedir . '/composer.phar')) {
40748 $this->composer = "$php $basedir/composer.phar";
40749 } else {
40750 $out = array();
40751 exec('which composer', $out);
40752 if (empty($out)) {
40753 throw new BuildException(
40754 'Could not determine composer location.'
40755 );
40756 }
40757 $this->composer = $out[0];
40758 }
40759 }
40760 }
40761 <?php
40762 /**
40763 * Phing wrapper around git subsplit.
40764 *
40765 * @see https://github.com/dflydev/git-subsplit
40766 * @copyright 2012 Clay Loveless <clay@php.net>
40767 * @license http://claylo.mit-license.org/2012/ MIT License
40768 */
40769
40770 require_once 'phing/tasks/ext/git/GitBaseTask.php';
40771
40772 // base - base of tree to split out
40773 // subIndicatorFile - composer.json, package.xml?
40774 class GuzzleSubSplitTask extends GitBaseTask
40775 {
40776 /**
40777 * What git repository to pull from and publish to
40778 */
40779 protected $remote = null;
40780
40781 /**
40782 * Publish for comma-separated heads instead of all heads
40783 */
40784 protected $heads = null;
40785
40786 /**
40787 * Publish for comma-separated tags instead of all tags
40788 */
40789 protected $tags = null;
40790
40791 /**
40792 * Base of the tree RELATIVE TO .subsplit working dir
40793 */
40794 protected $base = null;
40795
40796 /**
40797 * The presence of this file will indicate that the directory it resides
40798 * in is at the top level of a split.
40799 */
40800 protected $subIndicatorFile = 'composer.json';
40801
40802 /**
40803 * Do everything except actually send the update.
40804 */
40805 protected $dryRun = null;
40806
40807 /**
40808 * Do not sync any heads.
40809 */
40810 protected $noHeads = false;
40811
40812 /**
40813 * Do not sync any tags.
40814 */
40815 protected $noTags = false;
40816
40817 /**
40818 * The splits we found in the heads
40819 */
40820 protected $splits;
40821
40822 public function setRemote($str)
40823 {
40824 $this->remote = $str;
40825 }
40826
40827 public function getRemote()
40828 {
40829 return $this->remote;
40830 }
40831
40832 public function setHeads($str)
40833 {
40834 $this->heads = explode(',', $str);
40835 }
40836
40837 public function getHeads()
40838 {
40839 return $this->heads;
40840 }
40841
40842 public function setTags($str)
40843 {
40844 $this->tags = explode(',', $str);
40845 }
40846
40847 public function getTags()
40848 {
40849 return $this->tags;
40850 }
40851
40852 public function setBase($str)
40853 {
40854 $this->base = $str;
40855 }
40856
40857 public function getBase()
40858 {
40859 return $this->base;
40860 }
40861
40862 public function setSubIndicatorFile($str)
40863 {
40864 $this->subIndicatorFile = $str;
40865 }
40866
40867 public function getSubIndicatorFile()
40868 {
40869 return $this->subIndicatorFile;
40870 }
40871
40872 public function setDryRun($bool)
40873 {
40874 $this->dryRun = (bool) $bool;
40875 }
40876
40877 public function getDryRun()
40878 {
40879 return $this->dryRun;
40880 }
40881
40882 public function setNoHeads($bool)
40883 {
40884 $this->noHeads = (bool) $bool;
40885 }
40886
40887 public function getNoHeads()
40888 {
40889 return $this->noHeads;
40890 }
40891
40892 public function setNoTags($bool)
40893 {
40894 $this->noTags = (bool) $bool;
40895 }
40896
40897 public function getNoTags()
40898 {
40899 return $this->noTags;
40900 }
40901
40902 /**
40903 * GitClient from VersionControl_Git
40904 */
40905 protected $client = null;
40906
40907 /**
40908 * The main entry point
40909 */
40910 public function main()
40911 {
40912 $repo = $this->getRepository();
40913 if (empty($repo)) {
40914 throw new BuildException('"repository" is a required parameter');
40915 }
40916
40917 $remote = $this->getRemote();
40918 if (empty($remote)) {
40919 throw new BuildException('"remote" is a required parameter');
40920 }
40921
40922 chdir($repo);
40923 $this->client = $this->getGitClient(false, $repo);
40924
40925 // initalized yet?
40926 if (!is_dir('.subsplit')) {
40927 $this->subsplitInit();
40928 } else {
40929 // update
40930 $this->subsplitUpdate();
40931 }
40932
40933 // find all splits based on heads requested
40934 $this->findSplits();
40935
40936 // check that GitHub has the repos
40937 $this->verifyRepos();
40938
40939 // execute the subsplits
40940 $this->publish();
40941 }
40942
40943 public function publish()
40944 {
40945 $this->log('DRY RUN ONLY FOR NOW');
40946 $base = $this->getBase();
40947 $base = rtrim($base, '/') . '/';
40948 $org = $this->getOwningTarget()->getProject()->getProperty('github.org');
40949
40950 $splits = array();
40951
40952 $heads = $this->getHeads();
40953 foreach ($heads as $head) {
40954 foreach ($this->splits[$head] as $component => $meta) {
40955 $splits[] = $base . $component . ':git@github.com:'. $org.'/'.$meta['repo'];
40956 }
40957
40958 $cmd = 'git subsplit publish ';
40959 $cmd .= escapeshellarg(implode(' ', $splits));
40960
40961 if ($this->getNoHeads()) {
40962 $cmd .= ' --no-heads';
40963 } else {
40964 $cmd .= ' --heads='.$head;
40965 }
40966
40967 if ($this->getNoTags()) {
40968 $cmd .= ' --no-tags';
40969 } else {
40970 if ($this->getTags()) {
40971 $cmd .= ' --tags=' . escapeshellarg(implode(' ', $this->getTags()));
40972 }
40973 }
40974
40975 passthru($cmd);
40976 }
40977 }
40978
40979 /**
40980 * Runs `git subsplit update`
40981 */
40982 public function subsplitUpdate()
40983 {
40984 $repo = $this->getRepository();
40985 $this->log('git-subsplit update...');
40986 $cmd = $this->client->getCommand('subsplit');
40987 $cmd->addArgument('update');
40988 try {
40989 $output = $cmd->execute();
40990 } catch (Exception $e) {
40991 throw new BuildException('git subsplit update failed'. $e);
40992 }
40993 chdir($repo . '/.subsplit');
40994 passthru('php ../composer.phar update --dev');
40995 chdir($repo);
40996 }
40997
40998 /**
40999 * Runs `git subsplit init` based on the remote repository.
41000 */
41001 public function subsplitInit()
41002 {
41003 $remote = $this->getRemote();
41004 $cmd = $this->client->getCommand('subsplit');
41005 $this->log('running git-subsplit init ' . $remote);
41006
41007 $cmd->setArguments(array(
41008 'init',
41009 $remote
41010 ));
41011
41012 try {
41013 $output = $cmd->execute();
41014 } catch (Exception $e) {
41015 throw new BuildException('git subsplit init failed'. $e);
41016 }
41017 $this->log(trim($output), Project::MSG_INFO);
41018 $repo = $this->getRepository();
41019 chdir($repo . '/.subsplit');
41020 passthru('php ../composer.phar install --dev');
41021 chdir($repo);
41022 }
41023
41024 /**
41025 * Find the composer.json files using Phing's directory scanner
41026 *
41027 * @return array
41028 */
41029 protected function findSplits()
41030 {
41031 $this->log("checking heads for subsplits");
41032 $repo = $this->getRepository();
41033 $base = $this->getBase();
41034
41035 $splits = array();
41036 $heads = $this->getHeads();
41037
41038 if (!empty($base)) {
41039 $base = '/' . ltrim($base, '/');
41040 } else {
41041 $base = '/';
41042 }
41043
41044 chdir($repo . '/.subsplit');
41045 foreach ($heads as $head) {
41046 $splits[$head] = array();
41047
41048 // check each head requested *BEFORE* the actual subtree split command gets it
41049 passthru("git checkout '$head'");
41050 $ds = new DirectoryScanner();
41051 $ds->setBasedir($repo . '/.subsplit' . $base);
41052 $ds->setIncludes(array('**/'.$this->subIndicatorFile));
41053 $ds->scan();
41054 $files = $ds->getIncludedFiles();
41055
41056 // Process the files we found
41057 foreach ($files as $file) {
41058 $pkg = file_get_contents($repo . '/.subsplit' . $base .'/'. $file);
41059 $pkg_json = json_decode($pkg, true);
41060 $name = $pkg_json['name'];
41061 $component = str_replace('/composer.json', '', $file);
41062 // keep this for split cmd
41063 $tmpreponame = explode('/', $name);
41064 $reponame = $tmpreponame[1];
41065 $splits[$head][$component]['repo'] = $reponame;
41066 $nscomponent = str_replace('/', '\\', $component);
41067 $splits[$head][$component]['desc'] = "[READ ONLY] Subtree split of $nscomponent: " . $pkg_json['description'];
41068 }
41069 }
41070
41071 // go back to how we found it
41072 passthru("git checkout master");
41073 chdir($repo);
41074 $this->splits = $splits;
41075 }
41076
41077 /**
41078 * Based on list of repositories we determined we *should* have, talk
41079 * to GitHub and make sure they're all there.
41080 *
41081 */
41082 protected function verifyRepos()
41083 {
41084 $this->log('verifying GitHub target repos');
41085 $github_org = $this->getOwningTarget()->getProject()->getProperty('github.org');
41086 $github_creds = $this->getOwningTarget()->getProject()->getProperty('github.basicauth');
41087
41088 if ($github_creds == 'username:password') {
41089 $this->log('Skipping GitHub repo checks. Update github.basicauth in build.properties to verify repos.', 1);
41090 return;
41091 }
41092
41093 $ch = curl_init('https://api.github.com/orgs/'.$github_org.'/repos?type=all');
41094 curl_setopt($ch, CURLOPT_RETURNTRANSFER, 1);
41095 curl_setopt($ch, CURLOPT_USERPWD, $github_creds);
41096 // change this when we know we can use our bundled CA bundle!
41097 curl_setopt($ch, CURLOPT_SSL_VERIFYPEER, false);
41098 $result = curl_exec($ch);
41099 curl_close($ch);
41100 $repos = json_decode($result, true);
41101 $existing_repos = array();
41102
41103 // parse out the repos we found on GitHub
41104 foreach ($repos as $repo) {
41105 $tmpreponame = explode('/', $repo['full_name']);
41106 $reponame = $tmpreponame[1];
41107 $existing_repos[$reponame] = $repo['description'];
41108 }
41109
41110 $heads = $this->getHeads();
41111 foreach ($heads as $head) {
41112 foreach ($this->splits[$head] as $component => $meta) {
41113
41114 $reponame = $meta['repo'];
41115
41116 if (!isset($existing_repos[$reponame])) {
41117 $this->log("Creating missing repo $reponame");
41118 $payload = array(
41119 'name' => $reponame,
41120 'description' => $meta['desc'],
41121 'homepage' => 'http://www.guzzlephp.org/',
41122 'private' => true,
41123 'has_issues' => false,
41124 'has_wiki' => false,
41125 'has_downloads' => true,
41126 'auto_init' => false
41127 );
41128 $ch = curl_init('https://api.github.com/orgs/'.$github_org.'/repos');
41129 curl_setopt($ch, CURLOPT_RETURNTRANSFER, 1);
41130 curl_setopt($ch, CURLOPT_USERPWD, $github_creds);
41131 curl_setopt($ch, CURLOPT_HTTPHEADER, array('Content-Type: application/json'));
41132 curl_setopt($ch, CURLOPT_POST, 1);
41133 curl_setopt($ch, CURLOPT_POSTFIELDS, json_encode($payload));
41134 // change this when we know we can use our bundled CA bundle!
41135 curl_setopt($ch, CURLOPT_SSL_VERIFYPEER, false);
41136 $result = curl_exec($ch);
41137 echo "Response code: ".curl_getinfo($ch, CURLINFO_HTTP_CODE)."\n";
41138 curl_close($ch);
41139 } else {
41140 $this->log("Repo $reponame exists", 2);
41141 }
41142 }
41143 }
41144 }
41145 }
41146 <?php
41147 /**
41148 * Phing task for node server checking
41149 *
41150 * @copyright 2012 Clay Loveless <clay@php.net>
41151 * @license http://claylo.mit-license.org/2012/ MIT License
41152 */
41153
41154 require_once 'phing/Task.php';
41155
41156 class NodeServerTask extends Task
41157 {
41158 protected $cmd = null;
41159 protected $action = 'start';
41160 protected $serverfile = 'tests/Guzzle/Tests/Http/server.js';
41161
41162 /**
41163 * The setter for the start command
41164 *
41165 * @param string $str How to start the node server
41166 */
41167 public function setCmd($str)
41168 {
41169 $this->cmd = $str;
41170 }
41171
41172 public function getCmd()
41173 {
41174 return $this->cmd;
41175 }
41176
41177 /**
41178 * The setter for the action
41179 *
41180 * @param string $str Start up or shutdown
41181 */
41182 public function setAction($str)
41183 {
41184 $this->action = $str;
41185 }
41186 public function getAction()
41187 {
41188 return $this->action;
41189 }
41190
41191 public function main()
41192 {
41193 $cmd = $this->getCmd();
41194 $action = $this->getAction();
41195
41196 if (empty($cmd)) {
41197 throw new BuildException('"cmd" is a required parameter');
41198 }
41199
41200 if ($action == 'start') {
41201 $this->startServer();
41202 } else {
41203 $this->stopServer();
41204 }
41205 }
41206
41207 protected function startServer()
41208 {
41209 chdir(__DIR__ . '/../..');
41210 $serverfile = $this->serverfile;
41211 $cmd = $this->getCmd();
41212
41213 // resolve $HOME directory
41214 if ($cmd[0] == '~') {
41215 $cmd = $_ENV["HOME"] . substr($cmd, 1);
41216 }
41217
41218 $fp = @fsockopen('127.0.0.1', 8124, $errno, $errstr, 1);
41219 if (! $fp) {
41220 // need to start node server
41221 $cmd = escapeshellcmd($cmd . ' ' . $serverfile);
41222 $this->log('starting node test server with '.$cmd);
41223 exec($cmd . ' &> /dev/null &');
41224 sleep(2);
41225 $fp = @fsockopen('127.0.0.1', 8124, $errno, $errstr, 1);
41226 }
41227
41228 // test it again
41229 if (! $fp) {
41230 $this->log('could not start node server');
41231 } else {
41232 fclose($fp);
41233 $this->log('node test server running');
41234 }
41235 }
41236
41237 protected function stopServer()
41238 {
41239 exec('ps axo "pid,command"', $out);
41240 $nodeproc = false;
41241 foreach ($out as $proc) {
41242 if (strpos($proc, $this->serverfile) !== false) {
41243 $nodeproc = $proc;
41244 break;
41245 }
41246 }
41247
41248 if ($nodeproc) {
41249 $proc = trim($nodeproc);
41250 $space = strpos($proc, ' ');
41251 $pid = substr($proc, 0, $space);
41252
41253 $killed = posix_kill($pid, 9);
41254 if ($killed) {
41255 $this->log('test server stopped');
41256 } else {
41257 $this->log('test server appears immortal');
41258 }
41259 } else {
41260 $this->log('could not find test server in process list');
41261 }
41262 }
41263 }
41264 <?xml version="1.0" encoding="UTF-8"?>
41265 <project name="guzzle" default="test">
41266 <!-- set local values, like git location -->
41267 <property file="phing/build.properties" override="true" />
41268
41269 <property name="dir.output" value="${project.basedir}/build/artifacts" />
41270 <property name="dir.imports" value="${project.basedir}/phing/imports" />
41271 <property name="dir.bin" value="${project.basedir}/bin" />
41272 <property name="repo.dir" value="${project.basedir}" />
41273
41274 <import file="${dir.imports}/dependencies.xml"/>
41275 <import file="${dir.imports}/test.xml"/>
41276 <import file="${dir.imports}/deploy.xml"/>
41277 <!-- <import file="${dir.imports}/metrics.xml"/> -->
41278
41279 <target name="test" description="Run unit tests" depends="test-init">
41280 <trycatch>
41281 <try>
41282 <exec passthru="true" command="vendor/bin/phpunit" checkReturn="true" />
41283 </try>
41284 <finally>
41285 <testserver cmd="${cmd.testserver}" action="stop" />
41286 </finally>
41287 </trycatch>
41288 </target>
41289
41290 <target name="test-init" depends="install-dependencies" description="Initialize test dependencies">
41291 <testserver cmd="${cmd.testserver}" action="start" />
41292 <copy file="phpunit.xml.dist" tofile="phpunit.xml" overwrite="false" />
41293 </target>
41294
41295 <target name="build-init" description="Initialize local phing properties">
41296 <copy file="phing/build.properties.dist" tofile="phing/build.properties" overwrite="false" />
41297 </target>
41298
41299 <target name="clean">
41300 <delete dir="${dir.output}"/>
41301 <delete dir="${project.basedir}/build/pearwork"/>
41302 </target>
41303
41304 <target name="prepare" depends="clean,test-init,build-init">
41305 <mkdir dir="${dir.output}"/>
41306 <mkdir dir="${dir.output}/logs" />
41307 </target>
41308
41309 <target name="coverage" depends="prepare">
41310 <exec passthru="true" command="vendor/bin/phpunit --coverage-html=${dir.output}/coverage" />
41311 </target>
41312
41313 <target name="view-coverage">
41314 <exec passthru="true" command="open ${dir.output}/coverage/index.html" />
41315 </target>
41316
41317 </project>
41318 <?php
41319
41320 error_reporting(E_ALL | E_STRICT);
41321
41322 // Ensure that composer has installed all dependencies
41323 if (!file_exists(dirname(__DIR__) . '/composer.lock')) {
41324 die("Dependencies must be installed using composer:\n\nphp composer.phar install --dev\n\n"
41325 . "See http://getcomposer.org for help with installing composer\n");
41326 }
41327
41328 require_once 'PHPUnit/TextUI/TestRunner.php';
41329
41330 // Include the phar files if testing against the phars
41331 if (get_cfg_var('guzzle_phar')) {
41332 require get_cfg_var('guzzle_phar');
41333 }
41334
41335 // Include the composer autoloader
41336 $autoloader = require dirname(__DIR__) . '/vendor/autoload.php';
41337
41338 // Add the services file to the default service builder
41339 $servicesFile = __DIR__ . '/Guzzle/Tests/TestData/services/services.json';
41340 Guzzle\Tests\GuzzleTestCase::setServiceBuilder(Guzzle\Service\Builder\ServiceBuilder::factory($servicesFile));
41341 {
41342 "operations": {
41343 "abstract": {
41344 "uri": "/abstract"
41345 }
41346 }
41347 }
41348 {
41349 "includes": ["foo.json", "bar.json"]
41350 }{
41351 "includes": ["foo.json"]
41352 }{
41353 "includes": ["recursive.json"],
41354 "operations": {
41355 "abstract": {
41356 "httpMethod": "POST"
41357 }
41358 }
41359 }
41360 {
41361 "includes": ["foo.json"]
41362 }{
41363 "includes": [ "test_service2.json" ],
41364 "operations": {
41365 "test": {
41366 "uri": "/path"
41367 },
41368 "concrete": {
41369 "extends": "abstract"
41370 },
41371 "foo_bar": {
41372 "uri": "/testing",
41373 "parameters": {
41374 "other": {
41375 "location": "json",
41376 "location_key": "Other"
41377 },
41378 "test": {
41379 "type": "object",
41380 "location": "json",
41381 "properties": {
41382 "baz": {
41383 "type": "boolean",
41384 "default": true
41385 },
41386 "bar": {
41387 "type": "string",
41388 "filters": [
41389 {
41390 "method": "strtolower",
41391 "args": ["test", "@value"]
41392 },
41393 "strtoupper"
41394 ]
41395 }
41396 }
41397 }
41398 }
41399 }
41400 }
41401 }
41402 {
41403 "includes": [ "json2.json" ],
41404 "services": {
41405 "abstract": {
41406 "access_key": "xyz",
41407 "secret": "abc"
41408 },
41409 "mock": {
41410 "class": "Guzzle\\Tests\\Service\\Mock\\MockClient",
41411 "extends": "abstract",
41412 "params": {
41413 "username": "foo",
41414 "password": "baz",
41415 "subdomain": "bar"
41416 }
41417 }
41418 }
41419 }
41420 {
41421 "abstract": {
41422 "access_key": "xyz",
41423 "secret": "abc"
41424 },
41425 "mock": {
41426 "class": "Guzzle\\Tests\\Service\\Mock\\MockClient",
41427 "extends": "abstract",
41428 "params": {
41429 "username": "foo",
41430 "password": "baz",
41431 "subdomain": "bar"
41432 }
41433 },
41434
41435 "test.abstract.aws": {
41436 "params": {
41437 "access_key": "12345",
41438 "secret_key": "abcd"
41439 }
41440 },
41441
41442 "test.s3": {
41443 "class": "Guzzle\\Service\\Aws\\S3Client",
41444 "extends": "test.abstract.aws",
41445 "params": {
41446 "devpay_product_token": "",
41447 "devpay_user_token": ""
41448 }
41449 },
41450
41451 "test.simple_db": {
41452 "class": "Guzzle\\Service\\Aws\\SimpleDb\\SimpleDbClient",
41453 "extends": "test.abstract.aws"
41454 },
41455
41456 "test.sqs": {
41457 "class": "Guzzle\\Service\\Aws\\Sqs\\SqsClient",
41458 "extends": "test.abstract.aws"
41459 },
41460
41461 "test.centinel": {
41462 "class": "Guzzle\\Service\\CardinalCommerce\\Centinel.CentinelClient",
41463 "params": {
41464 "password": "test",
41465 "processor_id": "123",
41466 "merchant_id": "456"
41467 }
41468 },
41469
41470 "test.mws": {
41471 "class": "Guzzle\\Service\\Mws\\MwsClient",
41472 "extends": "test.abstract.aws",
41473 "params": {
41474 "merchant_id": "ABCDE",
41475 "marketplace_id": "FGHIJ",
41476 "application_name": "GuzzleTest",
41477 "application_version": "0.1",
41478 "base_url": "https://mws.amazonservices.com"
41479 }
41480 },
41481
41482 "mock": {
41483 "class": "Guzzle\\Tests\\Service\\Mock\\MockClient",
41484 "params": {
41485 "username": "test_user",
41486 "password": "****",
41487 "subdomain": "test"
41488 }
41489 }
41490 }
41491 {
41492 "services": {
41493 "foo": {
41494 "class": "Guzzle\\Tests\\Service\\Mock\\MockClient",
41495 "extends": "abstract",
41496 "params": {
41497 "baz": "bar"
41498 }
41499 }
41500 }
41501 }
41502 HTTP/1.1 200 OK
41503 Content-Length: 0
41504
41505 <?php
41506
41507 namespace Guzzle\Tests\Cache;
41508
41509 use Guzzle\Cache\Zf2CacheAdapter;
41510 use Zend\Cache\StorageFactory;
41511
41512 /**
41513 * @covers Guzzle\Cache\Zf2CacheAdapter
41514 */
41515 class Zf2CacheAdapterTest extends \Guzzle\Tests\GuzzleTestCase
41516 {
41517 private $cache;
41518 private $adapter;
41519
41520 /**
41521 * Prepares the environment before running a test.
41522 */
41523 protected function setUp()
41524 {
41525 parent::setUp();
41526 $this->cache = StorageFactory::factory(array(
41527 'adapter' => 'memory'
41528 ));
41529 $this->adapter = new Zf2CacheAdapter($this->cache);
41530 }
41531
41532 /**
41533 * Cleans up the environment after running a test.
41534 */
41535 protected function tearDown()
41536 {
41537 $this->adapter = null;
41538 $this->cache = null;
41539 parent::tearDown();
41540 }
41541
41542 public function testCachesDataUsingCallables()
41543 {
41544 $this->assertTrue($this->adapter->save('test', 'data', 1000));
41545 $this->assertEquals('data', $this->adapter->fetch('test'));
41546 }
41547
41548 public function testChecksIfCacheContainsKeys()
41549 {
41550 $this->adapter->save('test', 'data', 1000);
41551 $this->assertTrue($this->adapter->contains('test'));
41552 $this->assertFalse($this->adapter->contains('foo'));
41553 }
41554
41555 public function testDeletesFromCacheByKey()
41556 {
41557 $this->adapter->save('test', 'data', 1000);
41558 $this->assertTrue($this->adapter->contains('test'));
41559 $this->adapter->delete('test');
41560 $this->assertFalse($this->adapter->contains('test'));
41561 }
41562 }
41563 <?php
41564
41565 namespace Guzzle\Tests\Cache;
41566
41567 use Guzzle\Cache\CacheAdapterFactory;
41568 use Guzzle\Cache\DoctrineCacheAdapter;
41569 use Doctrine\Common\Cache\ArrayCache;
41570 use Zend\Cache\StorageFactory;
41571
41572 /**
41573 * @covers Guzzle\Cache\CacheAdapterFactory
41574 */
41575 class CacheAdapterFactoryTest extends \Guzzle\Tests\GuzzleTestCase
41576 {
41577 /** @var ArrayCache */
41578 private $cache;
41579
41580 /** @var DoctrineCacheAdapter */
41581 private $adapter;
41582
41583 /**
41584 * Prepares the environment before running a test.
41585 */
41586 protected function setup()
41587 {
41588 parent::setUp();
41589 $this->cache = new ArrayCache();
41590 $this->adapter = new DoctrineCacheAdapter($this->cache);
41591 }
41592
41593 /**
41594 * @expectedException \InvalidArgumentException
41595 */
41596 public function testEnsuresConfigIsObject()
41597 {
41598 CacheAdapterFactory::fromCache(array());
41599 }
41600
41601 /**
41602 * @expectedException \InvalidArgumentException
41603 */
41604 public function testEnsuresKnownType()
41605 {
41606 CacheAdapterFactory::fromCache(new \stdClass());
41607 }
41608
41609 public function cacheProvider()
41610 {
41611 return array(
41612 array(new DoctrineCacheAdapter(new ArrayCache()), 'Guzzle\Cache\DoctrineCacheAdapter'),
41613 array(new ArrayCache(), 'Guzzle\Cache\DoctrineCacheAdapter'),
41614 array(StorageFactory::factory(array('adapter' => 'memory')), 'Guzzle\Cache\Zf2CacheAdapter'),
41615 );
41616 }
41617
41618 /**
41619 * @dataProvider cacheProvider
41620 */
41621 public function testCreatesNullCacheAdapterByDefault($cache, $type)
41622 {
41623 $adapter = CacheAdapterFactory::fromCache($cache);
41624 $this->assertInstanceOf($type, $adapter);
41625 }
41626 }
41627 <?php
41628
41629 namespace Guzzle\Tests\Common\Cache;
41630
41631 use Guzzle\Cache\NullCacheAdapter;
41632
41633 /**
41634 * @covers Guzzle\Cache\NullCacheAdapter
41635 */
41636 class NullCacheAdapterTest extends \Guzzle\Tests\GuzzleTestCase
41637 {
41638 public function testNullCacheAdapter()
41639 {
41640 $c = new NullCacheAdapter();
41641 $this->assertEquals(false, $c->contains('foo'));
41642 $this->assertEquals(true, $c->delete('foo'));
41643 $this->assertEquals(false, $c->fetch('foo'));
41644 $this->assertEquals(true, $c->save('foo', 'bar'));
41645 }
41646 }
41647 <?php
41648
41649 namespace Guzzle\Tests\Cache;
41650
41651 use Guzzle\Cache\DoctrineCacheAdapter;
41652 use Doctrine\Common\Cache\ArrayCache;
41653
41654 /**
41655 * @covers Guzzle\Cache\DoctrineCacheAdapter
41656 * @covers Guzzle\Cache\AbstractCacheAdapter
41657 */
41658 class CacheAdapterTest extends \Guzzle\Tests\GuzzleTestCase
41659 {
41660 /** @var ArrayCache */
41661 private $cache;
41662
41663 /** @var DoctrineCacheAdapter */
41664 private $adapter;
41665
41666 /**
41667 * Prepares the environment before running a test.
41668 */
41669 protected function setUp()
41670 {
41671 parent::setUp();
41672 $this->cache = new ArrayCache();
41673 $this->adapter = new DoctrineCacheAdapter($this->cache);
41674 }
41675
41676 /**
41677 * Cleans up the environment after running a test.
41678 */
41679 protected function tearDown()
41680 {
41681 $this->adapter = null;
41682 $this->cache = null;
41683 parent::tearDown();
41684 }
41685
41686 public function testGetCacheObject()
41687 {
41688 $this->assertEquals($this->cache, $this->adapter->getCacheObject());
41689 }
41690
41691 public function testSave()
41692 {
41693 $this->assertTrue($this->adapter->save('test', 'data', 1000));
41694 }
41695
41696 public function testFetch()
41697 {
41698 $this->assertTrue($this->adapter->save('test', 'data', 1000));
41699 $this->assertEquals('data', $this->adapter->fetch('test'));
41700 }
41701
41702 public function testContains()
41703 {
41704 $this->assertTrue($this->adapter->save('test', 'data', 1000));
41705 $this->assertTrue($this->adapter->contains('test'));
41706 }
41707
41708 public function testDelete()
41709 {
41710 $this->assertTrue($this->adapter->save('test', 'data', 1000));
41711 $this->assertTrue($this->adapter->delete('test'));
41712 $this->assertFalse($this->adapter->contains('test'));
41713 }
41714 }
41715 <?php
41716
41717 namespace Guzzle\Tests\Cache;
41718
41719 use Guzzle\Cache\ClosureCacheAdapter;
41720
41721 /**
41722 * @covers Guzzle\Cache\ClosureCacheAdapter
41723 */
41724 class ClosureCacheAdapterTest extends \Guzzle\Tests\GuzzleTestCase
41725 {
41726 /** @var ClosureCacheAdapter */
41727 private $adapter;
41728
41729 /** Array of callables to use for testing */
41730 private $callables;
41731
41732 /** Cache data for testing */
41733 public $data = array();
41734
41735 /**
41736 * Prepares the environment before running a test.
41737 */
41738 protected function setUp()
41739 {
41740 parent::setUp();
41741
41742 $that = $this;
41743 $this->callables = array(
41744 'contains' => function($id, $options = array()) use ($that) {
41745 return array_key_exists($id, $that->data);
41746 },
41747 'delete' => function($id, $options = array()) use ($that) {
41748 unset($that->data[$id]);
41749 return true;
41750 },
41751 'fetch' => function($id, $options = array()) use ($that) {
41752 return array_key_exists($id, $that->data) ? $that->data[$id] : null;
41753 },
41754 'save' => function($id, $data, $lifeTime, $options = array()) use ($that) {
41755 $that->data[$id] = $data;
41756 return true;
41757 }
41758 );
41759
41760 $this->adapter = new ClosureCacheAdapter($this->callables);
41761 }
41762
41763 /**
41764 * Cleans up the environment after running a test.
41765 */
41766 protected function tearDown()
41767 {
41768 $this->cache = null;
41769 $this->callables = null;
41770 parent::tearDown();
41771 }
41772
41773 /**
41774 * @expectedException InvalidArgumentException
41775 */
41776 public function testEnsuresCallablesArePresent()
41777 {
41778 $callables = $this->callables;
41779 unset($callables['delete']);
41780 $cache = new ClosureCacheAdapter($callables);
41781 }
41782
41783 public function testAllCallablesMustBePresent()
41784 {
41785 $cache = new ClosureCacheAdapter($this->callables);
41786 }
41787
41788 public function testCachesDataUsingCallables()
41789 {
41790 $this->assertTrue($this->adapter->save('test', 'data', 1000));
41791 $this->assertEquals('data', $this->adapter->fetch('test'));
41792 }
41793
41794 public function testChecksIfCacheContainsKeys()
41795 {
41796 $this->adapter->save('test', 'data', 1000);
41797 $this->assertTrue($this->adapter->contains('test'));
41798 $this->assertFalse($this->adapter->contains('foo'));
41799 }
41800
41801 public function testDeletesFromCacheByKey()
41802 {
41803 $this->adapter->save('test', 'data', 1000);
41804 $this->assertTrue($this->adapter->contains('test'));
41805 $this->adapter->delete('test');
41806 $this->assertFalse($this->adapter->contains('test'));
41807 }
41808 }
41809 <?php
41810
41811 namespace Guzzle\Tests\Log;
41812
41813 use Guzzle\Log\ClosureLogAdapter;
41814
41815 /**
41816 * @covers Guzzle\Log\ClosureLogAdapter
41817 */
41818 class ClosureLogAdapterTest extends \Guzzle\Tests\GuzzleTestCase
41819 {
41820 public function testClosure()
41821 {
41822 $that = $this;
41823 $modified = null;
41824 $this->adapter = new ClosureLogAdapter(function($message, $priority, $extras = null) use ($that, &$modified) {
41825 $modified = array($message, $priority, $extras);
41826 });
41827 $this->adapter->log('test', LOG_NOTICE, 'localhost');
41828 $this->assertEquals(array('test', LOG_NOTICE, 'localhost'), $modified);
41829 }
41830
41831 /**
41832 * @expectedException InvalidArgumentException
41833 */
41834 public function testThrowsExceptionWhenNotCallable()
41835 {
41836 $this->adapter = new ClosureLogAdapter(123);
41837 }
41838 }
41839 <?php
41840
41841 namespace Guzzle\Tests\Log;
41842
41843 use Guzzle\Log\ArrayLogAdapter;
41844
41845 class ArrayLogAdapterTest extends \Guzzle\Tests\GuzzleTestCase
41846 {
41847 public function testLog()
41848 {
41849 $adapter = new ArrayLogAdapter();
41850 $adapter->log('test', \LOG_NOTICE, 'localhost');
41851 $this->assertEquals(array(array('message' => 'test', 'priority' => \LOG_NOTICE, 'extras' => 'localhost')), $adapter->getLogs());
41852 }
41853
41854 public function testClearLog()
41855 {
41856 $adapter = new ArrayLogAdapter();
41857 $adapter->log('test', \LOG_NOTICE, 'localhost');
41858 $adapter->clearLogs();
41859 $this->assertEquals(array(), $adapter->getLogs());
41860 }
41861 }
41862 <?php
41863
41864 namespace Guzzle\Tests\Log;
41865
41866 use Guzzle\Http\Client;
41867 use Guzzle\Http\Curl\CurlHandle;
41868 use Guzzle\Http\Message\EntityEnclosingRequest;
41869 use Guzzle\Http\EntityBody;
41870 use Guzzle\Http\Message\Response;
41871 use Guzzle\Log\MessageFormatter;
41872 use Guzzle\Plugin\Log\LogPlugin;
41873 use Guzzle\Log\ClosureLogAdapter;
41874
41875 /**
41876 * @covers Guzzle\Log\MessageFormatter
41877 */
41878 class MessageFormatterTest extends \Guzzle\Tests\GuzzleTestCase
41879 {
41880 protected $request;
41881 protected $response;
41882 protected $handle;
41883
41884 public function setUp()
41885 {
41886 $this->request = new EntityEnclosingRequest('POST', 'http://foo.com?q=test', array(
41887 'X-Foo' => 'bar',
41888 'Authorization' => 'Baz'
41889 ));
41890 $this->request->setBody(EntityBody::factory('Hello'));
41891
41892 $this->response = new Response(200, array(
41893 'X-Test' => 'Abc'
41894 ), 'Foo');
41895
41896 $this->handle = $this->getMockBuilder('Guzzle\Http\Curl\CurlHandle')
41897 ->disableOriginalConstructor()
41898 ->setMethods(array('getError', 'getErrorNo', 'getStderr', 'getInfo'))
41899 ->getMock();
41900
41901 $this->handle->expects($this->any())
41902 ->method('getError')
41903 ->will($this->returnValue('e'));
41904
41905 $this->handle->expects($this->any())
41906 ->method('getErrorNo')
41907 ->will($this->returnValue('123'));
41908
41909 $this->handle->expects($this->any())
41910 ->method('getStderr')
41911 ->will($this->returnValue('testing'));
41912
41913 $this->handle->expects($this->any())
41914 ->method('getInfo')
41915 ->will($this->returnValueMap(array(
41916 array(CURLINFO_CONNECT_TIME, '123'),
41917 array(CURLINFO_TOTAL_TIME, '456')
41918 )));
41919 }
41920
41921 public function logProvider()
41922 {
41923 return array(
41924 // Uses the cache for the second time
41925 array('{method} - {method}', 'POST - POST'),
41926 array('{url}', 'http://foo.com?q=test'),
41927 array('{port}', '80'),
41928 array('{resource}', '/?q=test'),
41929 array('{host}', 'foo.com'),
41930 array('{hostname}', gethostname()),
41931 array('{protocol}/{version}', 'HTTP/1.1'),
41932 array('{code} {phrase}', '200 OK'),
41933 array('{req_header_Foo}', ''),
41934 array('{req_header_X-Foo}', 'bar'),
41935 array('{req_header_Authorization}', 'Baz'),
41936 array('{res_header_foo}', ''),
41937 array('{res_header_X-Test}', 'Abc'),
41938 array('{req_body}', 'Hello'),
41939 array('{res_body}', 'Foo'),
41940 array('{curl_stderr}', 'testing'),
41941 array('{curl_error}', 'e'),
41942 array('{curl_code}', '123'),
41943 array('{connect_time}', '123'),
41944 array('{total_time}', '456')
41945 );
41946 }
41947
41948 /**
41949 * @dataProvider logProvider
41950 */
41951 public function testFormatsMessages($template, $output)
41952 {
41953 $formatter = new MessageFormatter($template);
41954 $this->assertEquals($output, $formatter->format($this->request, $this->response, $this->handle));
41955 }
41956
41957 public function testFormatsRequestsAndResponses()
41958 {
41959 $formatter = new MessageFormatter();
41960 $formatter->setTemplate('{request}{response}');
41961 $this->assertEquals($this->request . $this->response, $formatter->format($this->request, $this->response));
41962 }
41963
41964 public function testAddsTimestamp()
41965 {
41966 $formatter = new MessageFormatter('{ts}');
41967 $this->assertNotEmpty($formatter->format($this->request, $this->response));
41968 }
41969
41970 public function testUsesResponseWhenNoHandleAndGettingCurlInformation()
41971 {
41972 $formatter = new MessageFormatter('{connect_time}/{total_time}');
41973 $response = $this->getMockBuilder('Guzzle\Http\Message\Response')
41974 ->disableOriginalConstructor()
41975 ->setMethods(array('getInfo'))
41976 ->getMock();
41977 $response->expects($this->exactly(2))
41978 ->method('getInfo')
41979 ->will($this->returnValueMap(array(
41980 array('connect_time', '1'),
41981 array('total_time', '2'),
41982 )));
41983 $this->assertEquals('1/2', $formatter->format($this->request, $response));
41984 }
41985
41986 public function testUsesEmptyStringWhenNoHandleAndNoResponse()
41987 {
41988 $formatter = new MessageFormatter('{connect_time}/{total_time}');
41989 $this->assertEquals('/', $formatter->format($this->request));
41990 }
41991
41992 public function testInjectsTotalTime()
41993 {
41994 $out = '';
41995 $formatter = new MessageFormatter('{connect_time}/{total_time}');
41996 $adapter = new ClosureLogAdapter(function ($m) use (&$out) { $out .= $m; });
41997 $log = new LogPlugin($adapter, $formatter);
41998 $this->getServer()->enqueue("HTTP/1.1 200 OK\r\nContent-Length: 2\r\n\r\nHI");
41999 $client = new Client($this->getServer()->getUrl());
42000 $client->addSubscriber($log);
42001 $client->get('/')->send();
42002 $this->assertNotEquals('/', $out);
42003 }
42004 }
42005 <?php
42006
42007 namespace Guzzle\Tests\Log;
42008
42009 use Guzzle\Log\Zf2LogAdapter;
42010 use Zend\Log\Logger;
42011 use Zend\Log\Writer\Stream;
42012
42013 /**
42014 * @covers Guzzle\Log\Zf2LogAdapter
42015 */
42016 class Zf2LogAdapterTest extends \Guzzle\Tests\GuzzleTestCase
42017 {
42018 /** @var Zf2LogAdapter */
42019 protected $adapter;
42020
42021 /** @var Logger */
42022 protected $log;
42023
42024 /** @var resource */
42025 protected $stream;
42026
42027 protected function setUp()
42028 {
42029 $this->stream = fopen('php://temp', 'r+');
42030 $this->log = new Logger();
42031 $this->log->addWriter(new Stream($this->stream));
42032 $this->adapter = new Zf2LogAdapter($this->log);
42033
42034 }
42035
42036 public function testLogsMessagesToAdaptedObject()
42037 {
42038 // Test without a priority
42039 $this->adapter->log('Zend_Test!', \LOG_NOTICE);
42040 rewind($this->stream);
42041 $contents = stream_get_contents($this->stream);
42042 $this->assertEquals(1, substr_count($contents, 'Zend_Test!'));
42043
42044 // Test with a priority
42045 $this->adapter->log('Zend_Test!', \LOG_ALERT);
42046 rewind($this->stream);
42047 $contents = stream_get_contents($this->stream);
42048 $this->assertEquals(2, substr_count($contents, 'Zend_Test!'));
42049 }
42050
42051 public function testExposesAdaptedLogObject()
42052 {
42053 $this->assertEquals($this->log, $this->adapter->getLogObject());
42054 }
42055 }
42056 <?php
42057
42058 namespace Guzzle\Tests\Log;
42059
42060 use Guzzle\Log\PsrLogAdapter;
42061 use Monolog\Logger;
42062 use Monolog\Handler\TestHandler;
42063
42064 /**
42065 * @covers Guzzle\Log\PsrLogAdapter
42066 * @covers Guzzle\Log\AbstractLogAdapter
42067 */
42068 class PsrLogAdapterTest extends \Guzzle\Tests\GuzzleTestCase
42069 {
42070 public function testLogsMessagesToAdaptedObject()
42071 {
42072 $log = new Logger('test');
42073 $handler = new TestHandler();
42074 $log->pushHandler($handler);
42075 $adapter = new PsrLogAdapter($log);
42076 $adapter->log('test!', LOG_INFO);
42077 $this->assertTrue($handler->hasInfoRecords());
42078 $this->assertSame($log, $adapter->getLogObject());
42079 }
42080 }
42081 <?php
42082
42083 namespace Guzzle\Tests\Iterator;
42084
42085 use Guzzle\Iterator\AppendIterator;
42086
42087 /**
42088 * @covers Guzzle\Iterator\AppendIterator
42089 */
42090 class AppendIteratorTest extends \PHPUnit_Framework_TestCase
42091 {
42092 public function testTraversesIteratorsInOrder()
42093 {
42094 $a = new \ArrayIterator(array(
42095 'a' => 1,
42096 'b' => 2
42097 ));
42098 $b = new \ArrayIterator(array());
42099 $c = new \ArrayIterator(array(
42100 'c' => 3,
42101 'd' => 4
42102 ));
42103 $i = new AppendIterator();
42104 $i->append($a);
42105 $i->append($b);
42106 $i->append($c);
42107 $this->assertEquals(array(1, 2, 3, 4), iterator_to_array($i, false));
42108 }
42109 }
42110 <?php
42111
42112 namespace Guzzle\Tests\Iterator;
42113
42114 use Guzzle\Iterator\FilterIterator;
42115
42116 /**
42117 * @covers Guzzle\Iterator\FilterIterator
42118 */
42119 class FilterIteratorTest extends \PHPUnit_Framework_TestCase
42120 {
42121 public function testFiltersValues()
42122 {
42123 $i = new FilterIterator(new \ArrayIterator(range(0, 100)), function ($value) {
42124 return $value % 2;
42125 });
42126
42127 $this->assertEquals(range(1, 99, 2), iterator_to_array($i, false));
42128 }
42129
42130 /**
42131 * @expectedException \InvalidArgumentException
42132 */
42133 public function testValidatesCallable()
42134 {
42135 $i = new FilterIterator(new \ArrayIterator(), new \stdClass());
42136 }
42137 }
42138 <?php
42139
42140 namespace Guzzle\Tests\Iterator;
42141
42142 use Guzzle\Iterator\ChunkedIterator;
42143
42144 /**
42145 * @covers Guzzle\Iterator\ChunkedIterator
42146 */
42147 class ChunkedIteratorTest extends \PHPUnit_Framework_TestCase
42148 {
42149 public function testChunksIterator()
42150 {
42151 $chunked = new ChunkedIterator(new \ArrayIterator(range(0, 100)), 10);
42152 $chunks = iterator_to_array($chunked, false);
42153 $this->assertEquals(11, count($chunks));
42154 foreach ($chunks as $j => $chunk) {
42155 $this->assertEquals(range($j * 10, min(100, $j * 10 + 9)), $chunk);
42156 }
42157 }
42158
42159 public function testChunksIteratorWithOddValues()
42160 {
42161 $chunked = new ChunkedIterator(new \ArrayIterator(array(1, 2, 3, 4, 5)), 2);
42162 $chunks = iterator_to_array($chunked, false);
42163 $this->assertEquals(3, count($chunks));
42164 $this->assertEquals(array(1, 2), $chunks[0]);
42165 $this->assertEquals(array(3, 4), $chunks[1]);
42166 $this->assertEquals(array(5), $chunks[2]);
42167 }
42168
42169 /**
42170 * @test
42171 * @runInSeparateProcess
42172 */
42173 public function mustNotTerminateWithTraversable()
42174 {
42175 $traversable = simplexml_load_string('<root><foo/><foo/><foo/></root>')->foo;
42176 $chunked = new ChunkedIterator($traversable, 2);
42177 $actual = iterator_to_array($chunked, false);
42178 $this->assertCount(2, $actual);
42179 }
42180
42181 /**
42182 * @test
42183 */
42184 public function sizeOfZeroMakesIteratorInvalid() {
42185 $chunked = new ChunkedIterator(new \ArrayIterator(range(1, 5)), 0);
42186 $chunked->rewind();
42187 $this->assertFalse($chunked->valid());
42188 }
42189
42190 /**
42191 * @test
42192 * @expectedException \InvalidArgumentException
42193 */
42194 public function sizeLowerZeroThrowsException() {
42195 $chunked = new ChunkedIterator(new \ArrayIterator(range(1, 5)), -1);
42196 }
42197 }
42198 <?php
42199
42200 namespace Guzzle\Tests\Iterator;
42201
42202 use Guzzle\Iterator\MethodProxyIterator;
42203 use Guzzle\Iterator\ChunkedIterator;
42204
42205 /**
42206 * @covers Guzzle\Iterator\MethodProxyIterator
42207 */
42208 class MethodProxyIteratorTest extends \PHPUnit_Framework_TestCase
42209 {
42210 public function testProxiesMagicCallsToInnermostIterator()
42211 {
42212 $i = new \ArrayIterator();
42213 $proxy = new MethodProxyIterator(new MethodProxyIterator(new MethodProxyIterator($i)));
42214 $proxy->append('a');
42215 $proxy->append('b');
42216 $this->assertEquals(array('a', 'b'), $i->getArrayCopy());
42217 $this->assertEquals(array('a', 'b'), $proxy->getArrayCopy());
42218 }
42219
42220 public function testUsesInnerIterator()
42221 {
42222 $i = new MethodProxyIterator(new ChunkedIterator(new \ArrayIterator(array(1, 2, 3, 4, 5)), 2));
42223 $this->assertEquals(3, count(iterator_to_array($i, false)));
42224 }
42225 }
42226 <?php
42227
42228 namespace Guzzle\Tests\Iterator;
42229
42230 use Guzzle\Iterator\MapIterator;
42231
42232 /**
42233 * @covers Guzzle\Iterator\MapIterator
42234 */
42235 class MapIteratorTest extends \PHPUnit_Framework_TestCase
42236 {
42237 public function testFiltersValues()
42238 {
42239 $i = new MapIterator(new \ArrayIterator(range(0, 100)), function ($value) {
42240 return $value * 10;
42241 });
42242
42243 $this->assertEquals(range(0, 1000, 10), iterator_to_array($i, false));
42244 }
42245
42246 /**
42247 * @expectedException \InvalidArgumentException
42248 */
42249 public function testValidatesCallable()
42250 {
42251 $i = new MapIterator(new \ArrayIterator(), new \stdClass());
42252 }
42253 }
42254 <?php
42255
42256 namespace Guzzle\Tests\Plugin\CurlAuth;
42257
42258 use Guzzle\Common\Version;
42259 use Guzzle\Plugin\CurlAuth\CurlAuthPlugin;
42260 use Guzzle\Http\Client;
42261
42262 /**
42263 * @covers Guzzle\Plugin\CurlAuth\CurlAuthPlugin
42264 */
42265 class CurlAuthPluginTest extends \Guzzle\Tests\GuzzleTestCase
42266 {
42267 public function testAddsBasicAuthentication()
42268 {
42269 Version::$emitWarnings = false;
42270 $plugin = new CurlAuthPlugin('michael', 'test');
42271 $client = new Client('http://www.test.com/');
42272 $client->getEventDispatcher()->addSubscriber($plugin);
42273 $request = $client->get('/');
42274 $this->assertEquals('michael', $request->getUsername());
42275 $this->assertEquals('test', $request->getPassword());
42276 Version::$emitWarnings = true;
42277 }
42278
42279 public function testAddsDigestAuthentication()
42280 {
42281 Version::$emitWarnings = false;
42282 $plugin = new CurlAuthPlugin('julian', 'test', CURLAUTH_DIGEST);
42283 $client = new Client('http://www.test.com/');
42284 $client->getEventDispatcher()->addSubscriber($plugin);
42285 $request = $client->get('/');
42286 $this->assertEquals('julian', $request->getUsername());
42287 $this->assertEquals('test', $request->getPassword());
42288 $this->assertEquals('julian:test', $request->getCurlOptions()->get(CURLOPT_USERPWD));
42289 $this->assertEquals(CURLAUTH_DIGEST, $request->getCurlOptions()->get(CURLOPT_HTTPAUTH));
42290 Version::$emitWarnings = true;
42291 }
42292 }
42293 <?php
42294
42295 namespace Guzzle\Tests\Plugin\Async;
42296
42297 use Guzzle\Plugin\Async\AsyncPlugin;
42298 use Guzzle\Http\Message\RequestInterface;
42299 use Guzzle\Http\Message\RequestFactory;
42300 use Guzzle\Http\Curl\CurlHandle;
42301 use Guzzle\Http\Exception\CurlException;
42302 use Guzzle\Common\Event;
42303 use Guzzle\Http\Client;
42304
42305 /**
42306 * @covers Guzzle\Plugin\Async\AsyncPlugin
42307 */
42308 class AsyncPluginTest extends \Guzzle\Tests\GuzzleTestCase
42309 {
42310 public function testSubscribesToEvents()
42311 {
42312 $events = AsyncPlugin::getSubscribedEvents();
42313 $this->assertArrayHasKey('request.before_send', $events);
42314 $this->assertArrayHasKey('request.exception', $events);
42315 $this->assertArrayHasKey('curl.callback.progress', $events);
42316 }
42317
42318 public function testEnablesProgressCallbacks()
42319 {
42320 $p = new AsyncPlugin();
42321 $request = RequestFactory::getInstance()->create('PUT', 'http://www.example.com');
42322 $event = new Event(array(
42323 'request' => $request
42324 ));
42325 $p->onBeforeSend($event);
42326 $this->assertEquals(true, $request->getCurlOptions()->get('progress'));
42327 }
42328
42329 public function testAddsTimesOutAfterSending()
42330 {
42331 $p = new AsyncPlugin();
42332 $request = RequestFactory::getInstance()->create('PUT', 'http://www.example.com');
42333 $handle = CurlHandle::factory($request);
42334 $event = new Event(array(
42335 'request' => $request,
42336 'handle' => $handle->getHandle(),
42337 'uploaded' => 10,
42338 'upload_size' => 10,
42339 'downloaded' => 0
42340 ));
42341 $p->onCurlProgress($event);
42342 }
42343
42344 public function testEnsuresRequestIsSet()
42345 {
42346 $p = new AsyncPlugin();
42347 $event = new Event(array(
42348 'uploaded' => 10,
42349 'upload_size' => 10,
42350 'downloaded' => 0
42351 ));
42352 $p->onCurlProgress($event);
42353 }
42354
42355 public function testMasksCurlExceptions()
42356 {
42357 $p = new AsyncPlugin();
42358 $request = RequestFactory::getInstance()->create('PUT', 'http://www.example.com');
42359 $e = new CurlException('Error');
42360 $event = new Event(array(
42361 'request' => $request,
42362 'exception' => $e
42363 ));
42364 $p->onRequestTimeout($event);
42365 $this->assertEquals(RequestInterface::STATE_COMPLETE, $request->getState());
42366 $this->assertEquals(200, $request->getResponse()->getStatusCode());
42367 $this->assertTrue($request->getResponse()->hasHeader('X-Guzzle-Async'));
42368 }
42369
42370 public function testEnsuresIntegration()
42371 {
42372 $this->getServer()->flush();
42373 $this->getServer()->enqueue("HTTP/1.1 204 FOO\r\nContent-Length: 4\r\n\r\ntest");
42374 $client = new Client($this->getServer()->getUrl());
42375 $request = $client->post('/', null, array(
42376 'foo' => 'bar'
42377 ));
42378 $request->getEventDispatcher()->addSubscriber(new AsyncPlugin());
42379 $request->send();
42380 $this->assertEquals('', $request->getResponse()->getBody(true));
42381 $this->assertTrue($request->getResponse()->hasHeader('X-Guzzle-Async'));
42382 $received = $this->getServer()->getReceivedRequests(true);
42383 $this->assertEquals('POST', $received[0]->getMethod());
42384 }
42385 }
42386 <?php
42387
42388 namespace Guzzle\Tests\Plugin\Md5;
42389
42390 use Guzzle\Common\Event;
42391 use Guzzle\Plugin\Md5\CommandContentMd5Plugin;
42392 use Guzzle\Service\Description\ServiceDescription;
42393 use Guzzle\Service\Client;
42394
42395 /**
42396 * @covers Guzzle\Plugin\Md5\CommandContentMd5Plugin
42397 */
42398 class CommandContentMd5PluginTest extends \Guzzle\Tests\GuzzleTestCase
42399 {
42400 protected function getClient()
42401 {
42402 $description = new ServiceDescription(array(
42403 'operations' => array(
42404 'test' => array(
42405 'httpMethod' => 'PUT',
42406 'parameters' => array(
42407 'ContentMD5' => array(),
42408 'Body' => array(
42409 'location' => 'body'
42410 )
42411 )
42412 )
42413 )
42414 ));
42415
42416 $client = new Client();
42417 $client->setDescription($description);
42418
42419 return $client;
42420 }
42421
42422 public function testHasEvents()
42423 {
42424 $this->assertNotEmpty(CommandContentMd5Plugin::getSubscribedEvents());
42425 }
42426
42427 public function testValidatesMd5WhenParamExists()
42428 {
42429 $client = $this->getClient();
42430 $command = $client->getCommand('test', array(
42431 'Body' => 'Foo',
42432 'ContentMD5' => true
42433 ));
42434 $event = new Event(array('command' => $command));
42435 $request = $command->prepare();
42436 $plugin = new CommandContentMd5Plugin();
42437 $plugin->onCommandBeforeSend($event);
42438 $this->assertEquals('E1bGfXrRY42Ba/uCLdLCXQ==', (string) $request->getHeader('Content-MD5'));
42439 }
42440
42441 public function testDoesNothingWhenNoPayloadExists()
42442 {
42443 $client = $this->getClient();
42444 $client->getDescription()->getOperation('test')->setHttpMethod('GET');
42445 $command = $client->getCommand('test');
42446 $event = new Event(array('command' => $command));
42447 $request = $command->prepare();
42448 $plugin = new CommandContentMd5Plugin();
42449 $plugin->onCommandBeforeSend($event);
42450 $this->assertNull($request->getHeader('Content-MD5'));
42451 }
42452
42453 public function testAddsValidationToResponsesOfContentMd5()
42454 {
42455 $client = $this->getClient();
42456 $client->getDescription()->getOperation('test')->setHttpMethod('GET');
42457 $command = $client->getCommand('test', array(
42458 'ValidateMD5' => true
42459 ));
42460 $event = new Event(array('command' => $command));
42461 $request = $command->prepare();
42462 $plugin = new CommandContentMd5Plugin();
42463 $plugin->onCommandBeforeSend($event);
42464 $listeners = $request->getEventDispatcher()->getListeners('request.complete');
42465 $this->assertNotEmpty($listeners);
42466 }
42467
42468 public function testIgnoresValidationWhenDisabled()
42469 {
42470 $client = $this->getClient();
42471 $client->getDescription()->getOperation('test')->setHttpMethod('GET');
42472 $command = $client->getCommand('test', array(
42473 'ValidateMD5' => false
42474 ));
42475 $event = new Event(array('command' => $command));
42476 $request = $command->prepare();
42477 $plugin = new CommandContentMd5Plugin();
42478 $plugin->onCommandBeforeSend($event);
42479 $listeners = $request->getEventDispatcher()->getListeners('request.complete');
42480 $this->assertEmpty($listeners);
42481 }
42482 }
42483 <?php
42484
42485 namespace Guzzle\Tests\Plugin\Md5;
42486
42487 use Guzzle\Http\EntityBody;
42488 use Guzzle\Http\Message\RequestFactory;
42489 use Guzzle\Http\Message\Response;
42490 use Guzzle\Plugin\Md5\Md5ValidatorPlugin;
42491
42492 /**
42493 * @covers Guzzle\Plugin\Md5\Md5ValidatorPlugin
42494 */
42495 class Md5ValidatorPluginTest extends \Guzzle\Tests\GuzzleTestCase
42496 {
42497 public function testValidatesMd5()
42498 {
42499 $plugin = new Md5ValidatorPlugin();
42500 $request = RequestFactory::getInstance()->create('GET', 'http://www.test.com/');
42501 $request->getEventDispatcher()->addSubscriber($plugin);
42502
42503 $body = 'abc';
42504 $hash = md5($body);
42505 $response = new Response(200, array(
42506 'Content-MD5' => $hash,
42507 'Content-Length' => 3
42508 ), 'abc');
42509
42510 $request->dispatch('request.complete', array(
42511 'response' => $response
42512 ));
42513
42514 // Try again with no Content-MD5
42515 $response->removeHeader('Content-MD5');
42516 $request->dispatch('request.complete', array(
42517 'response' => $response
42518 ));
42519 }
42520
42521 /**
42522 * @expectedException UnexpectedValueException
42523 */
42524 public function testThrowsExceptionOnInvalidMd5()
42525 {
42526 $plugin = new Md5ValidatorPlugin();
42527 $request = RequestFactory::getInstance()->create('GET', 'http://www.test.com/');
42528 $request->getEventDispatcher()->addSubscriber($plugin);
42529
42530 $request->dispatch('request.complete', array(
42531 'response' => new Response(200, array(
42532 'Content-MD5' => 'foobar',
42533 'Content-Length' => 3
42534 ), 'abc')
42535 ));
42536 }
42537
42538 public function testSkipsWhenContentLengthIsTooLarge()
42539 {
42540 $plugin = new Md5ValidatorPlugin(false, 1);
42541 $request = RequestFactory::getInstance()->create('GET', 'http://www.test.com/');
42542 $request->getEventDispatcher()->addSubscriber($plugin);
42543
42544 $request->dispatch('request.complete', array(
42545 'response' => new Response(200, array(
42546 'Content-MD5' => 'foobar',
42547 'Content-Length' => 3
42548 ), 'abc')
42549 ));
42550 }
42551
42552 public function testProperlyValidatesWhenUsingContentEncoding()
42553 {
42554 $plugin = new Md5ValidatorPlugin(true);
42555 $request = RequestFactory::getInstance()->create('GET', 'http://www.test.com/');
42556 $request->getEventDispatcher()->addSubscriber($plugin);
42557
42558 // Content-MD5 is the MD5 hash of the canonical content after all
42559 // content-encoding has been applied. Because cURL will automatically
42560 // decompress entity bodies, we need to re-compress it to calculate.
42561 $body = EntityBody::factory('abc');
42562 $body->compress();
42563 $hash = $body->getContentMd5();
42564 $body->uncompress();
42565
42566 $response = new Response(200, array(
42567 'Content-MD5' => $hash,
42568 'Content-Encoding' => 'gzip'
42569 ), 'abc');
42570 $request->dispatch('request.complete', array(
42571 'response' => $response
42572 ));
42573 $this->assertEquals('abc', $response->getBody(true));
42574
42575 // Try again with an unknown encoding
42576 $response = new Response(200, array(
42577 'Content-MD5' => $hash,
42578 'Content-Encoding' => 'foobar'
42579 ), 'abc');
42580 $request->dispatch('request.complete', array(
42581 'response' => $response
42582 ));
42583
42584 // Try again with compress
42585 $body->compress('bzip2.compress');
42586 $response = new Response(200, array(
42587 'Content-MD5' => $body->getContentMd5(),
42588 'Content-Encoding' => 'compress'
42589 ), 'abc');
42590 $request->dispatch('request.complete', array(
42591 'response' => $response
42592 ));
42593
42594 // Try again with encoding and disabled content-encoding checks
42595 $request->getEventDispatcher()->removeSubscriber($plugin);
42596 $plugin = new Md5ValidatorPlugin(false);
42597 $request->getEventDispatcher()->addSubscriber($plugin);
42598 $request->dispatch('request.complete', array(
42599 'response' => $response
42600 ));
42601 }
42602 }
42603 <?php
42604
42605 namespace Guzzle\Tests\Plugin\Cache;
42606
42607 use Guzzle\Cache\DoctrineCacheAdapter;
42608 use Guzzle\Http\Message\Request;
42609 use Guzzle\Http\Message\RequestFactory;
42610 use Guzzle\Http\Message\Response;
42611 use Guzzle\Plugin\Cache\DefaultCacheStorage;
42612 use Doctrine\Common\Cache\ArrayCache;
42613
42614 /**
42615 * @covers Guzzle\Plugin\Cache\DefaultCacheStorage
42616 */
42617 class DefaultCacheStorageTest extends \Guzzle\Tests\GuzzleTestCase
42618 {
42619 protected function getCache()
42620 {
42621 $a = new ArrayCache();
42622 $c = new DoctrineCacheAdapter($a);
42623 $s = new DefaultCacheStorage($c);
42624 $request = new Request('GET', 'http://foo.com', array('Accept' => 'application/json'));
42625 $response = new Response(200, array(
42626 'Content-Type' => 'application/json',
42627 'Connection' => 'close',
42628 'X-Foo' => 'Bar',
42629 'Vary' => 'Accept'
42630 ), 'test');
42631 $s->cache($request, $response);
42632 $data = $this->readAttribute($a, 'data');
42633
42634 return array(
42635 'cache' => $a,
42636 'adapter' => $c,
42637 'storage' => $s,
42638 'request' => $request,
42639 'response' => $response,
42640 'serialized' => end($data)
42641 );
42642 }
42643
42644 public function testReturnsNullForCacheMiss()
42645 {
42646 $cache = $this->getCache();
42647 $this->assertNull($cache['storage']->fetch(new Request('GET', 'http://test.com')));
42648 }
42649
42650 public function testCachesRequests()
42651 {
42652 $cache = $this->getCache();
42653 $foundRequest = $foundBody = $bodyKey = false;
42654 foreach ($this->readAttribute($cache['cache'], 'data') as $key => $v) {
42655 if (strpos($v, 'foo.com')) {
42656 $foundRequest = true;
42657 $data = unserialize($v);
42658 $bodyKey = $data[0][3];
42659 $this->assertInternalType('integer', $data[0][4]);
42660 $this->assertFalse(isset($data[0][0]['connection']));
42661 $this->assertEquals('foo.com', $data[0][0]['host']);
42662 } elseif ($v == 'test') {
42663 $foundBody = $key;
42664 }
42665 }
42666 $this->assertContains($bodyKey, $foundBody);
42667 $this->assertTrue($foundRequest);
42668 }
42669
42670 public function testFetchesResponse()
42671 {
42672 $cache = $this->getCache();
42673 $response = $cache['storage']->fetch($cache['request']);
42674 $this->assertEquals(200, $response->getStatusCode());
42675 $this->assertFalse($response->hasHeader('Connection'));
42676 $this->assertEquals('Bar', (string) $response->getHeader('X-Foo'));
42677 $this->assertEquals('test', (string) $response->getBody());
42678 $this->assertTrue(in_array($cache['serialized'], $this->readAttribute($cache['cache'], 'data')));
42679 }
42680
42681 public function testDeletesRequestItemsAndBody()
42682 {
42683 $cache = $this->getCache();
42684 $cache['storage']->delete($cache['request']);
42685 $this->assertFalse(in_array('test', $this->readAttribute($cache['cache'], 'data')));
42686 $this->assertFalse(in_array($cache['serialized'], $this->readAttribute($cache['cache'], 'data')));
42687 }
42688
42689 public function testCachesMultipleRequestsWithVary()
42690 {
42691 $cache = $this->getCache();
42692 $cache['request']->setHeader('Accept', 'application/xml');
42693 $response = $cache['response']->setHeader('Content-Type', 'application/xml');
42694 $response->setBody('123');
42695 $cache['storage']->cache($cache['request'], $response);
42696 $data = $this->readAttribute($cache['cache'], 'data');
42697 foreach ($data as $v) {
42698 if (strpos($v, 'foo.com')) {
42699 $u = unserialize($v);
42700 $this->assertEquals(2, count($u));
42701 $this->assertEquals($u[0][0]['accept'], 'application/xml');
42702 $this->assertEquals($u[0][1]['content-type'], 'application/xml');
42703 $this->assertEquals($u[1][0]['accept'], 'application/json');
42704 $this->assertEquals($u[1][1]['content-type'], 'application/json');
42705 $this->assertNotSame($u[0][3], $u[1][3]);
42706 break;
42707 }
42708 }
42709 }
42710
42711 public function testPurgeRemovesAllMethodCaches()
42712 {
42713 $cache = $this->getCache();
42714 foreach (array('HEAD', 'POST', 'PUT', 'DELETE') as $method) {
42715 $request = RequestFactory::getInstance()->cloneRequestWithMethod($cache['request'], $method);
42716 $cache['storage']->cache($request, $cache['response']);
42717 }
42718 $cache['storage']->purge('http://foo.com');
42719 $this->assertFalse(in_array('test', $this->readAttribute($cache['cache'], 'data')));
42720 $this->assertFalse(in_array($cache['serialized'], $this->readAttribute($cache['cache'], 'data')));
42721 $this->assertEquals(
42722 array('DoctrineNamespaceCacheKey[]'),
42723 array_keys($this->readAttribute($cache['cache'], 'data'))
42724 );
42725 }
42726
42727 public function testRemovesExpiredResponses()
42728 {
42729 $cache = $this->getCache();
42730 $request = new Request('GET', 'http://xyz.com');
42731 $response = new Response(200, array('Age' => 1000, 'Cache-Control' => 'max-age=-10000'));
42732 $cache['storage']->cache($request, $response);
42733 $this->assertNull($cache['storage']->fetch($request));
42734 $data = $this->readAttribute($cache['cache'], 'data');
42735 $this->assertFalse(in_array('xyz.com', $data));
42736 $this->assertTrue(in_array($cache['serialized'], $data));
42737 }
42738
42739 public function testUsesVaryToDetermineResult()
42740 {
42741 $cache = $this->getCache();
42742 $this->assertInstanceOf('Guzzle\Http\Message\Response', $cache['storage']->fetch($cache['request']));
42743 $request = new Request('GET', 'http://foo.com', array('Accept' => 'application/xml'));
42744 $this->assertNull($cache['storage']->fetch($request));
42745 }
42746
42747 public function testEnsuresResponseIsStillPresent()
42748 {
42749 $cache = $this->getCache();
42750 $data = $this->readAttribute($cache['cache'], 'data');
42751 $key = array_search('test', $data);
42752 $cache['cache']->delete(substr($key, 1, -4));
42753 $this->assertNull($cache['storage']->fetch($cache['request']));
42754 }
42755
42756 public function staleProvider()
42757 {
42758 return array(
42759 array(
42760 new Request('GET', 'http://foo.com', array('Accept' => 'foo')),
42761 new Response(200, array('Cache-Control' => 'stale-if-error=100', 'Vary' => 'Accept'))
42762 ),
42763 array(
42764 new Request('GET', 'http://foo.com', array('Accept' => 'foo')),
42765 new Response(200, array('Cache-Control' => 'stale-if-error', 'Vary' => 'Accept'))
42766 )
42767 );
42768 }
42769
42770 /**
42771 * @dataProvider staleProvider
42772 */
42773 public function testUsesStaleTimeDirectiveForTtd($request, $response)
42774 {
42775 $cache = $this->getCache();
42776 $cache['storage']->cache($request, $response);
42777 $data = $this->readAttribute($cache['cache'], 'data');
42778 foreach ($data as $v) {
42779 if (strpos($v, 'foo.com')) {
42780 $u = unserialize($v);
42781 $this->assertGreaterThan($u[1][4], $u[0][4]);
42782 break;
42783 }
42784 }
42785 }
42786
42787 public function testCanFilterCacheKeys()
42788 {
42789 $cache = $this->getCache();
42790 $cache['request']->getQuery()->set('auth', 'foo');
42791 $this->assertNull($cache['storage']->fetch($cache['request']));
42792 $cache['request']->getParams()->set('cache.key_filter', 'auth');
42793 $this->assertNotNull($cache['storage']->fetch($cache['request']));
42794 }
42795 }
42796 <?php
42797
42798 namespace Guzzle\Tests\Plugin\Cache;
42799
42800 use Guzzle\Common\Event;
42801 use Guzzle\Common\Version;
42802 use Guzzle\Cache\DoctrineCacheAdapter;
42803 use Guzzle\Http\Client;
42804 use Guzzle\Http\Message\Request;
42805 use Guzzle\Http\Message\Response;
42806 use Guzzle\Plugin\Cache\CachePlugin;
42807 use Guzzle\Plugin\Cache\DefaultCacheStorage;
42808 use Guzzle\Plugin\Cache\CallbackCanCacheStrategy;
42809 use Doctrine\Common\Cache\ArrayCache;
42810
42811 /**
42812 * @group server
42813 * @covers Guzzle\Plugin\Cache\CachePlugin
42814 * @covers Guzzle\Plugin\Cache\DefaultRevalidation
42815 */
42816 class CachePluginTest extends \Guzzle\Tests\GuzzleTestCase
42817 {
42818 public function testAddsDefaultStorage()
42819 {
42820 $plugin = new CachePlugin();
42821 $this->assertInstanceOf('Guzzle\Plugin\Cache\CacheStorageInterface', $this->readAttribute($plugin, 'storage'));
42822 }
42823
42824 public function testAddsDefaultCollaborators()
42825 {
42826 $this->assertNotEmpty(CachePlugin::getSubscribedEvents());
42827 $plugin = new CachePlugin(array(
42828 'storage' => $this->getMockBuilder('Guzzle\Plugin\Cache\CacheStorageInterface')->getMockForAbstractClass()
42829 ));
42830 $this->assertInstanceOf('Guzzle\Plugin\Cache\CacheStorageInterface', $this->readAttribute($plugin, 'storage'));
42831 $this->assertInstanceOf(
42832 'Guzzle\Plugin\Cache\CanCacheStrategyInterface',
42833 $this->readAttribute($plugin, 'canCache')
42834 );
42835 $this->assertInstanceOf(
42836 'Guzzle\Plugin\Cache\RevalidationInterface',
42837 $this->readAttribute($plugin, 'revalidation')
42838 );
42839 }
42840
42841 public function testAddsCallbackCollaborators()
42842 {
42843 $this->assertNotEmpty(CachePlugin::getSubscribedEvents());
42844 $plugin = new CachePlugin(array('can_cache' => function () {}));
42845 $this->assertInstanceOf(
42846 'Guzzle\Plugin\Cache\CallbackCanCacheStrategy',
42847 $this->readAttribute($plugin, 'canCache')
42848 );
42849 }
42850
42851 public function testCanPassCacheAsOnlyArgumentToConstructor()
42852 {
42853 $p = new CachePlugin(new DoctrineCacheAdapter(new ArrayCache()));
42854 $p = new CachePlugin(new DefaultCacheStorage(new DoctrineCacheAdapter(new ArrayCache())));
42855 }
42856
42857 public function testUsesCreatedCacheStorage()
42858 {
42859 $plugin = new CachePlugin(array(
42860 'adapter' => $this->getMockBuilder('Guzzle\Cache\CacheAdapterInterface')->getMockForAbstractClass()
42861 ));
42862 $this->assertInstanceOf('Guzzle\Plugin\Cache\CacheStorageInterface', $this->readAttribute($plugin, 'storage'));
42863 }
42864
42865 public function testUsesProvidedOptions()
42866 {
42867 $can = $this->getMockBuilder('Guzzle\Plugin\Cache\CanCacheStrategyInterface')->getMockForAbstractClass();
42868 $revalidate = $this->getMockBuilder('Guzzle\Plugin\Cache\RevalidationInterface')->getMockForAbstractClass();
42869 $plugin = new CachePlugin(array(
42870 'storage' => $this->getMockBuilder('Guzzle\Plugin\Cache\CacheStorageInterface')->getMockForAbstractClass(),
42871 'can_cache' => $can,
42872 'revalidation' => $revalidate
42873 ));
42874 $this->assertSame($can, $this->readAttribute($plugin, 'canCache'));
42875 $this->assertSame($revalidate, $this->readAttribute($plugin, 'revalidation'));
42876 }
42877
42878 public function satisfyProvider()
42879 {
42880 $req1 = new Request('GET', 'http://foo.com', array('Cache-Control' => 'no-cache'));
42881
42882 return array(
42883 // The response is too old to satisfy the request
42884 array(new Request('GET', 'http://foo.com', array('Cache-Control' => 'max-age=20')), new Response(200, array('Age' => 100)), false, false),
42885 // The response cannot satisfy the request because it is stale
42886 array(new Request('GET', 'http://foo.com'), new Response(200, array('Cache-Control' => 'max-age=10', 'Age' => 100)), false, false),
42887 // Allows the expired response to satisfy the request because of the max-stale
42888 array(new Request('GET', 'http://foo.com', array('Cache-Control' => 'max-stale=15')), new Response(200, array('Cache-Control' => 'max-age=90', 'Age' => 100)), true, false),
42889 // Max stale is > than the allowed staleness
42890 array(new Request('GET', 'http://foo.com', array('Cache-Control' => 'max-stale=5')), new Response(200, array('Cache-Control' => 'max-age=90', 'Age' => 100)), false, false),
42891 // Performs cache revalidation
42892 array($req1, new Response(200), true, true),
42893 // Performs revalidation due to ETag on the response and no cache-control on the request
42894 array(new Request('GET', 'http://foo.com'), new Response(200, array(
42895 'ETag' => 'ABC',
42896 'Expires' => date('c', strtotime('+1 year'))
42897 )), true, true),
42898 );
42899 }
42900
42901 /**
42902 * @dataProvider satisfyProvider
42903 */
42904 public function testChecksIfResponseCanSatisfyRequest($request, $response, $can, $revalidates)
42905 {
42906 $didRevalidate = false;
42907 $storage = $this->getMockBuilder('Guzzle\Plugin\Cache\CacheStorageInterface')->getMockForAbstractClass();
42908 $revalidate = $this->getMockBuilder('Guzzle\Plugin\Cache\DefaultRevalidation')
42909 ->setMethods(array('revalidate'))
42910 ->setConstructorArgs(array($storage))
42911 ->getMockForAbstractClass();
42912
42913 $revalidate->expects($this->any())
42914 ->method('revalidate')
42915 ->will($this->returnCallback(function () use (&$didRevalidate) {
42916 $didRevalidate = true;
42917 return true;
42918 }));
42919
42920 $plugin = new CachePlugin(array(
42921 'storage' => $storage,
42922 'revalidation' => $revalidate
42923 ));
42924
42925 $this->assertEquals($can, $plugin->canResponseSatisfyRequest($request, $response));
42926 $this->assertEquals($didRevalidate, $revalidates);
42927 }
42928
42929 public function satisfyFailedProvider()
42930 {
42931 return array(
42932 // Neither has stale-if-error
42933 array(new Request('GET', 'http://foo.com', array()), new Response(200, array('Age' => 100)), false),
42934 // Request has stale-if-error
42935 array(new Request('GET', 'http://foo.com', array('Cache-Control' => 'stale-if-error')), new Response(200, array('Age' => 100, 'Cache-Control' => 'max-age=50')), true),
42936 // Request has valid stale-if-error
42937 array(new Request('GET', 'http://foo.com', array('Cache-Control' => 'stale-if-error=50')), new Response(200, array('Age' => 100, 'Cache-Control' => 'max-age=50')), true),
42938 // Request has expired stale-if-error
42939 array(new Request('GET', 'http://foo.com', array('Cache-Control' => 'stale-if-error=20')), new Response(200, array('Age' => 100, 'Cache-Control' => 'max-age=50')), false),
42940 // Response has permanent stale-if-error
42941 array(new Request('GET', 'http://foo.com', array()), new Response(200, array('Age' => 100, 'Cache-Control' => 'max-age=50, stale-if-error', )), true),
42942 // Response has valid stale-if-error
42943 array(new Request('GET', 'http://foo.com', array()), new Response(200, array('Age' => 100, 'Cache-Control' => 'max-age=50, stale-if-error=50')), true),
42944 // Response has expired stale-if-error
42945 array(new Request('GET', 'http://foo.com', array()), new Response(200, array('Age' => 100, 'Cache-Control' => 'max-age=50, stale-if-error=20')), false),
42946 // Request has valid stale-if-error but response does not
42947 array(new Request('GET', 'http://foo.com', array('Cache-Control' => 'stale-if-error=50')), new Response(200, array('Age' => 100, 'Cache-Control' => 'max-age=50, stale-if-error=20')), false),
42948 // Response has valid stale-if-error but request does not
42949 array(new Request('GET', 'http://foo.com', array('Cache-Control' => 'stale-if-error=20')), new Response(200, array('Age' => 100, 'Cache-Control' => 'max-age=50, stale-if-error=50')), false),
42950 );
42951 }
42952
42953 /**
42954 * @dataProvider satisfyFailedProvider
42955 */
42956 public function testChecksIfResponseCanSatisfyFailedRequest($request, $response, $can)
42957 {
42958 $plugin = new CachePlugin();
42959
42960 $this->assertEquals($can, $plugin->canResponseSatisfyFailedRequest($request, $response));
42961 }
42962
42963 public function testDoesNothingWhenRequestIsNotCacheable()
42964 {
42965 $storage = $this->getMockBuilder('Guzzle\Plugin\Cache\CacheStorageInterface')
42966 ->setMethods(array('fetch'))
42967 ->getMockForAbstractClass();
42968 $storage->expects($this->never())->method('fetch');
42969
42970 $plugin = new CachePlugin(array(
42971 'storage' => $storage,
42972 'can_cache' => new CallbackCanCacheStrategy(function () { return false; })
42973 ));
42974
42975 $plugin->onRequestBeforeSend(new Event(array(
42976 'request' => new Request('GET', 'http://foo.com')
42977 )));
42978 }
42979
42980 public function satisfiableProvider()
42981 {
42982 $date = new \DateTime('-10 seconds');
42983
42984 return array(
42985 // Fresh response
42986 array(new Response(200, array(), 'foo')),
42987 // Stale response
42988 array(new Response(200, array('Date' => $date->format('c'), 'Cache-Control' => 'max-age=5'), 'foo'))
42989 );
42990 }
42991
42992 /**
42993 * @dataProvider satisfiableProvider
42994 */
42995 public function testInjectsSatisfiableResponses($response)
42996 {
42997 $storage = $this->getMockBuilder('Guzzle\Plugin\Cache\CacheStorageInterface')
42998 ->setMethods(array('fetch'))
42999 ->getMockForAbstractClass();
43000
43001 $storage->expects($this->once())->method('fetch')->will($this->returnValue($response));
43002 $plugin = new CachePlugin(array('storage' => $storage));
43003 $request = new Request('GET', 'http://foo.com', array('Cache-Control' => 'max-stale'));
43004 $plugin->onRequestBeforeSend(new Event(array('request' => $request)));
43005 $plugin->onRequestSent(new Event(array('request' => $request, 'response' => $request->getResponse())));
43006 $this->assertEquals($response->getStatusCode(), $request->getResponse()->getStatusCode());
43007 $this->assertEquals((string) $response->getBody(), (string) $request->getResponse()->getBody());
43008 $this->assertTrue($request->getResponse()->hasHeader('Age'));
43009 if ($request->getResponse()->isFresh() === false) {
43010 $this->assertContains('110', (string) $request->getResponse()->getHeader('Warning'));
43011 }
43012 $this->assertSame(
43013 sprintf('%s GuzzleCache/%s', $request->getProtocolVersion(), Version::VERSION),
43014 (string) $request->getHeader('Via')
43015 );
43016 $this->assertSame(
43017 sprintf('%s GuzzleCache/%s',$request->getProtocolVersion(), Version::VERSION),
43018 (string) $request->getResponse()->getHeader('Via')
43019 );
43020 $this->assertTrue($request->getParams()->get('cache.lookup'));
43021 $this->assertTrue($request->getParams()->get('cache.hit'));
43022 $this->assertTrue($request->getResponse()->hasHeader('X-Cache-Lookup'));
43023 $this->assertTrue($request->getResponse()->hasHeader('X-Cache'));
43024 $this->assertEquals('HIT from GuzzleCache', (string) $request->getResponse()->getHeader('X-Cache'));
43025 $this->assertEquals('HIT from GuzzleCache', (string) $request->getResponse()->getHeader('X-Cache-Lookup'));
43026 }
43027
43028 public function satisfiableOnErrorProvider()
43029 {
43030 $date = new \DateTime('-10 seconds');
43031 return array(
43032 array(
43033 new Response(200, array(
43034 'Date' => $date->format('c'),
43035 'Cache-Control' => 'max-age=5, stale-if-error'
43036 ), 'foo'),
43037 )
43038 );
43039 }
43040
43041 /**
43042 * @dataProvider satisfiableOnErrorProvider
43043 */
43044 public function testInjectsSatisfiableResponsesOnError($cacheResponse)
43045 {
43046 $storage = $this->getMockBuilder('Guzzle\Plugin\Cache\CacheStorageInterface')
43047 ->setMethods(array('fetch'))
43048 ->getMockForAbstractClass();
43049 $storage->expects($this->exactly(2))->method('fetch')->will($this->returnValue($cacheResponse));
43050 $plugin = new CachePlugin(array('storage' => $storage));
43051 $request = new Request('GET', 'http://foo.com', array('Cache-Control' => 'max-stale'));
43052 $plugin->onRequestBeforeSend(new Event(array('request' => $request)));
43053 $plugin->onRequestError(
43054 $event = new Event(array(
43055 'request' => $request,
43056 'response' => $request->getResponse(),
43057 ))
43058 );
43059 $response = $event['response'];
43060 $this->assertEquals($cacheResponse->getStatusCode(), $response->getStatusCode());
43061 $this->assertEquals((string) $cacheResponse->getBody(), (string) $response->getBody());
43062 $this->assertTrue($response->hasHeader('Age'));
43063 if ($response->isFresh() === false) {
43064 $this->assertContains('110', (string) $response->getHeader('Warning'));
43065 }
43066 $this->assertSame(sprintf('%s GuzzleCache/%s', $request->getProtocolVersion(), Version::VERSION), (string) $request->getHeader('Via'));
43067 $this->assertSame(sprintf('%s GuzzleCache/%s', $request->getProtocolVersion(), Version::VERSION), (string) $response->getHeader('Via'));
43068 $this->assertTrue($request->getParams()->get('cache.lookup'));
43069 $this->assertSame('error', $request->getParams()->get('cache.hit'));
43070 $this->assertTrue($response->hasHeader('X-Cache-Lookup'));
43071 $this->assertTrue($response->hasHeader('X-Cache'));
43072 $this->assertEquals('HIT from GuzzleCache', (string) $response->getHeader('X-Cache-Lookup'));
43073 $this->assertEquals('HIT_ERROR from GuzzleCache', (string) $response->getHeader('X-Cache'));
43074 }
43075
43076 /**
43077 * @dataProvider satisfiableOnErrorProvider
43078 */
43079 public function testInjectsSatisfiableResponsesOnException($cacheResponse)
43080 {
43081 $storage = $this->getMockBuilder('Guzzle\Plugin\Cache\CacheStorageInterface')
43082 ->setMethods(array('fetch'))
43083 ->getMockForAbstractClass();
43084 $storage->expects($this->exactly(2))->method('fetch')->will($this->returnValue($cacheResponse));
43085 $plugin = new CachePlugin(array('storage' => $storage));
43086 $request = new Request('GET', 'http://foo.com', array('Cache-Control' => 'max-stale'));
43087 $plugin->onRequestBeforeSend(new Event(array(
43088 'request' => $request
43089 )));
43090 $plugin->onRequestException(
43091 new Event(array(
43092 'request' => $request,
43093 'response' => $request->getResponse(),
43094 'exception' => $this->getMock('Guzzle\Http\Exception\CurlException'),
43095 ))
43096 );
43097 $plugin->onRequestSent(
43098 new Event(array(
43099 'request' => $request,
43100 'response' => $response = $request->getResponse(),
43101 ))
43102 );
43103 $this->assertEquals($cacheResponse->getStatusCode(), $response->getStatusCode());
43104 $this->assertEquals((string) $cacheResponse->getBody(), (string) $response->getBody());
43105 $this->assertTrue($response->hasHeader('Age'));
43106 if ($response->isFresh() === false) {
43107 $this->assertContains('110', (string) $response->getHeader('Warning'));
43108 }
43109 $this->assertSame(sprintf('%s GuzzleCache/%s', $request->getProtocolVersion(), Version::VERSION), (string) $request->getHeader('Via'));
43110 $this->assertSame(sprintf('%s GuzzleCache/%s', $request->getProtocolVersion(), Version::VERSION), (string) $response->getHeader('Via'));
43111 $this->assertTrue($request->getParams()->get('cache.lookup'));
43112 $this->assertSame('error', $request->getParams()->get('cache.hit'));
43113 $this->assertTrue($response->hasHeader('X-Cache-Lookup'));
43114 $this->assertTrue($response->hasHeader('X-Cache'));
43115 $this->assertEquals('HIT from GuzzleCache', (string) $response->getHeader('X-Cache-Lookup'));
43116 $this->assertEquals('HIT_ERROR from GuzzleCache', (string) $response->getHeader('X-Cache'));
43117 }
43118
43119 public function unsatisfiableOnErrorProvider()
43120 {
43121 $date = new \DateTime('-10 seconds');
43122
43123 return array(
43124 // no-store on request
43125 array(
43126 false,
43127 array('Cache-Control' => 'no-store'),
43128 new Response(200, array('Date' => $date->format('D, d M Y H:i:s T'), 'Cache-Control' => 'max-age=5, stale-if-error'), 'foo'),
43129 ),
43130 // request expired
43131 array(
43132 true,
43133 array('Cache-Control' => 'stale-if-error=4'),
43134 new Response(200, array('Date' => $date->format('D, d M Y H:i:s T'), 'Cache-Control' => 'max-age=5, stale-if-error'), 'foo'),
43135 ),
43136 // response expired
43137 array(
43138 true,
43139 array('Cache-Control' => 'stale-if-error'),
43140 new Response(200, array('Date' => $date->format('D, d M Y H:i:s T'), 'Cache-Control' => 'max-age=5, stale-if-error=4'), 'foo'),
43141 ),
43142 );
43143 }
43144
43145 /**
43146 * @dataProvider unsatisfiableOnErrorProvider
43147 */
43148 public function testDoesNotInjectUnsatisfiableResponsesOnError($requestCanCache, $requestHeaders, $cacheResponse)
43149 {
43150 $storage = $this->getMockBuilder('Guzzle\Plugin\Cache\CacheStorageInterface')
43151 ->setMethods(array('fetch'))
43152 ->getMockForAbstractClass();
43153 $storage->expects($this->exactly($requestCanCache ? 2 : 0))->method('fetch')->will($this->returnValue($cacheResponse));
43154 $plugin = new CachePlugin(array('storage' => $storage));
43155 $request = new Request('GET', 'http://foo.com', $requestHeaders);
43156 $plugin->onRequestBeforeSend(new Event(array(
43157 'request' => $request
43158 )));
43159 $plugin->onRequestError(
43160 $event = new Event(array(
43161 'request' => $request,
43162 'response' => $response = $request->getResponse(),
43163 ))
43164 );
43165
43166 $this->assertSame($response, $event['response']);
43167 }
43168
43169 /**
43170 * @dataProvider unsatisfiableOnErrorProvider
43171 */
43172 public function testDoesNotInjectUnsatisfiableResponsesOnException($requestCanCache, $requestHeaders, $responseParts)
43173 {
43174 $storage = $this->getMockBuilder('Guzzle\Plugin\Cache\CacheStorageInterface')
43175 ->setMethods(array('fetch'))
43176 ->getMockForAbstractClass();
43177 $storage->expects($this->exactly($requestCanCache ? 2 : 0))->method('fetch')->will($this->returnValue($responseParts));
43178 $plugin = new CachePlugin(array('storage' => $storage));
43179 $request = new Request('GET', 'http://foo.com', $requestHeaders);
43180 $plugin->onRequestBeforeSend(new Event(array(
43181 'request' => $request
43182 )));
43183 $plugin->onRequestException(
43184 $event = new Event(array(
43185 'request' => $request,
43186 'response' => $response = $request->getResponse(),
43187 'exception' => $this->getMock('Guzzle\Http\Exception\CurlException'),
43188 ))
43189 );
43190
43191 $this->assertSame($response, $request->getResponse());
43192 }
43193
43194 public function testCachesResponsesWhenCacheable()
43195 {
43196 $cache = new ArrayCache();
43197 $plugin = new CachePlugin($cache);
43198
43199 $request = new Request('GET', 'http://foo.com');
43200 $response = new Response(200, array(), 'Foo');
43201 $plugin->onRequestBeforeSend(new Event(array(
43202 'request' => $request
43203 )));
43204 $plugin->onRequestSent(new Event(array(
43205 'request' => $request,
43206 'response' => $response
43207 )));
43208 $data = $this->readAttribute($cache, 'data');
43209 $this->assertNotEmpty($data);
43210 }
43211
43212 public function testPurgesRequests()
43213 {
43214 $storage = $this->getMockBuilder('Guzzle\Plugin\Cache\CacheStorageInterface')
43215 ->setMethods(array('purge'))
43216 ->getMockForAbstractClass();
43217 $storage->expects($this->atLeastOnce())->method('purge');
43218 $plugin = new CachePlugin(array('storage' => $storage));
43219 $request = new Request('GET', 'http://foo.com', array('X-Foo' => 'Bar'));
43220 $plugin->purge($request);
43221 }
43222
43223 public function testAutoPurgesRequests()
43224 {
43225 $storage = $this->getMockBuilder('Guzzle\Plugin\Cache\CacheStorageInterface')
43226 ->setMethods(array('purge'))
43227 ->getMockForAbstractClass();
43228 $storage->expects($this->atLeastOnce())->method('purge');
43229 $plugin = new CachePlugin(array('storage' => $storage, 'auto_purge' => true));
43230 $client = new Client();
43231 $request = $client->put('http://foo.com', array('X-Foo' => 'Bar'));
43232 $request->addSubscriber($plugin);
43233 $request->setResponse(new Response(200), true);
43234 $request->send();
43235 }
43236 }
43237 <?php
43238
43239 namespace Guzzle\Tests\Plugin\Cache;
43240
43241 use Guzzle\Http\Message\Request;
43242 use Guzzle\Http\Message\Response;
43243 use Guzzle\Plugin\Cache\SkipRevalidation;
43244
43245 /**
43246 * @covers Guzzle\Plugin\Cache\SkipRevalidation
43247 */
43248 class SkipRevalidationTest extends \Guzzle\Tests\GuzzleTestCase
43249 {
43250 public function testSkipsRequestRevalidation()
43251 {
43252 $skip = new SkipRevalidation();
43253 $this->assertTrue($skip->revalidate(new Request('GET', 'http://foo.com'), new Response(200)));
43254 }
43255 }
43256 <?php
43257
43258 namespace Guzzle\Tests\Plugin\Cache;
43259
43260 use Doctrine\Common\Cache\ArrayCache;
43261 use Guzzle\Cache\DoctrineCacheAdapter;
43262 use Guzzle\Common\Event;
43263 use Guzzle\Http\Message\Request;
43264 use Guzzle\Http\Message\Response;
43265 use Guzzle\Plugin\Cache\CachePlugin;
43266 use Guzzle\Plugin\Cache\CallbackCanCacheStrategy;
43267
43268 /**
43269 * @covers Guzzle\Plugin\Cache\CallbackCanCacheStrategy
43270 */
43271 class CallbackCanCacheStrategyTest extends \Guzzle\Tests\GuzzleTestCase
43272 {
43273 /**
43274 * @expectedException \Guzzle\Common\Exception\InvalidArgumentException
43275 */
43276 public function testConstructorEnsuresCallbackIsCallable()
43277 {
43278 $p = new CallbackCanCacheStrategy(new \stdClass());
43279 }
43280
43281 public function testUsesCallback()
43282 {
43283 $c = new CallbackCanCacheStrategy(function ($request) { return true; });
43284 $this->assertTrue($c->canCacheRequest(new Request('DELETE', 'http://www.foo.com')));
43285 }
43286
43287 /**
43288 * The following is a bit of an integration test to ensure that the CachePlugin honors a
43289 * custom can cache strategy.
43290 */
43291 public function testIntegrationWithCachePlugin()
43292 {
43293 $c = new CallbackCanCacheStrategy(
43294 function ($request) { return true; },
43295 function ($response) { return true; }
43296 );
43297
43298 // Make a request and response that have no business being cached
43299 $request = new Request('DELETE', 'http://www.foo.com');
43300 $response = Response::fromMessage(
43301 "HTTP/1.1 200 OK\r\n"
43302 . "Expires: Mon, 26 Jul 1997 05:00:00 GMT\r\n"
43303 . "Last-Modified: Wed, 09 Jan 2013 08:48:53 GMT\r\n"
43304 . "Content-Length: 2\r\n"
43305 . "Cache-Control: no-store, no-cache, must-revalidate, post-check=0, pre-check=0\r\n\r\n"
43306 . "hi"
43307 );
43308
43309 $this->assertTrue($c->canCacheRequest($request));
43310 $this->assertTrue($c->canCacheResponse($response));
43311
43312 $s = $this->getMockBuilder('Guzzle\Plugin\Cache\DefaultCacheStorage')
43313 ->setConstructorArgs(array(new DoctrineCacheAdapter(new ArrayCache())))
43314 ->setMethods(array('fetch'))
43315 ->getMockForAbstractClass();
43316
43317 $s->expects($this->once())
43318 ->method('fetch')
43319 ->will($this->returnValue($response));
43320
43321 $plugin = new CachePlugin(array('can_cache' => $c, 'storage' => $s));
43322 $plugin->onRequestBeforeSend(new Event(array('request' => $request)));
43323
43324 $this->assertEquals(200, $request->getResponse()->getStatusCode());
43325 $this->assertEquals('hi', $request->getResponse()->getBody(true));
43326 }
43327 }
43328 <?php
43329
43330 namespace Guzzle\Tests\Plugin\Cache;
43331
43332 use Guzzle\Http\Message\Request;
43333 use Guzzle\Http\Message\Response;
43334 use Guzzle\Plugin\Cache\DefaultCanCacheStrategy;
43335
43336 /**
43337 * @covers Guzzle\Plugin\Cache\DefaultCanCacheStrategy
43338 */
43339 class DefaultCanCacheStrategyTest extends \Guzzle\Tests\GuzzleTestCase
43340 {
43341 public function testReturnsRequestcanCacheRequest()
43342 {
43343 $strategy = new DefaultCanCacheStrategy();
43344 $request = new Request('GET', 'http://foo.com');
43345 $this->assertTrue($strategy->canCacheRequest($request));
43346 }
43347
43348 public function testDoesNotCacheNoStore()
43349 {
43350 $strategy = new DefaultCanCacheStrategy();
43351 $request = new Request('GET', 'http://foo.com', array('cache-control' => 'no-store'));
43352 $this->assertFalse($strategy->canCacheRequest($request));
43353 }
43354
43355 public function testCanCacheResponse()
43356 {
43357 $response = $this->getMockBuilder('Guzzle\Http\Message\Response')
43358 ->setMethods(array('canCache'))
43359 ->setConstructorArgs(array(200))
43360 ->getMock();
43361 $response->expects($this->once())
43362 ->method('canCache')
43363 ->will($this->returnValue(true));
43364 $strategy = new DefaultCanCacheStrategy();
43365 $this->assertTrue($strategy->canCacheResponse($response));
43366 }
43367 }
43368 <?php
43369
43370 namespace Guzzle\Tests\Plugin\Cache;
43371
43372 use Guzzle\Http\Message\Request;
43373 use Guzzle\Http\Message\Response;
43374 use Guzzle\Plugin\Cache\DenyRevalidation;
43375
43376 /**
43377 * @covers Guzzle\Plugin\Cache\DenyRevalidation
43378 */
43379 class DenyRevalidationTest extends \Guzzle\Tests\GuzzleTestCase
43380 {
43381 public function testDeniesRequestRevalidation()
43382 {
43383 $deny = new DenyRevalidation();
43384 $this->assertFalse($deny->revalidate(new Request('GET', 'http://foo.com'), new Response(200)));
43385 }
43386 }
43387 <?php
43388
43389 namespace Guzzle\Tests\Plugin\Cache;
43390
43391 use Guzzle\Http\Client;
43392 use Guzzle\Http\ClientInterface;
43393 use Guzzle\Http\Exception\BadResponseException;
43394 use Guzzle\Http\Exception\CurlException;
43395 use Guzzle\Http\Message\Request;
43396 use Guzzle\Http\Message\Response;
43397 use Guzzle\Http\Message\RequestFactory;
43398 use Guzzle\Plugin\Cache\CachePlugin;
43399 use Guzzle\Cache\DoctrineCacheAdapter;
43400 use Doctrine\Common\Cache\ArrayCache;
43401 use Guzzle\Plugin\Cache\DefaultCacheStorage;
43402 use Guzzle\Plugin\Mock\MockPlugin;
43403 use Guzzle\Tests\Http\Server;
43404
43405 /**
43406 * @covers Guzzle\Plugin\Cache\DefaultRevalidation
43407 * @group server
43408 */
43409 class DefaultRevalidationTest extends \Guzzle\Tests\GuzzleTestCase
43410 {
43411 protected function getHttpDate($time)
43412 {
43413 return gmdate(ClientInterface::HTTP_DATE, strtotime($time));
43414 }
43415
43416 /**
43417 * Data provider to test cache revalidation
43418 *
43419 * @return array
43420 */
43421 public function cacheRevalidationDataProvider()
43422 {
43423 return array(
43424 // Forces revalidation that passes
43425 array(
43426 true,
43427 "Pragma: no-cache\r\n\r\n",
43428 "HTTP/1.1 200 OK\r\nDate: " . $this->getHttpDate('-100 hours') . "\r\nContent-Length: 4\r\n\r\nData",
43429 "HTTP/1.1 304 NOT MODIFIED\r\nCache-Control: max-age=2000000\r\nContent-Length: 0\r\n\r\n",
43430 ),
43431 // Forces revalidation that overwrites what is in cache
43432 array(
43433 false,
43434 "\r\n",
43435 "HTTP/1.1 200 OK\r\nCache-Control: must-revalidate, no-cache\r\nDate: " . $this->getHttpDate('-10 hours') . "\r\nContent-Length: 4\r\n\r\nData",
43436 "HTTP/1.1 200 OK\r\nContent-Length: 5\r\n\r\nDatas",
43437 "HTTP/1.1 200 OK\r\nContent-Length: 5\r\nDate: " . $this->getHttpDate('now') . "\r\n\r\nDatas"
43438 ),
43439 // Throws an exception during revalidation
43440 array(
43441 false,
43442 "\r\n",
43443 "HTTP/1.1 200 OK\r\nCache-Control: no-cache\r\nDate: " . $this->getHttpDate('-3 hours') . "\r\n\r\nData",
43444 "HTTP/1.1 500 INTERNAL SERVER ERROR\r\nContent-Length: 0\r\n\r\n"
43445 ),
43446 // ETag mismatch
43447 array(
43448 false,
43449 "\r\n",
43450 "HTTP/1.1 200 OK\r\nCache-Control: no-cache\r\nETag: \"123\"\r\nDate: " . $this->getHttpDate('-10 hours') . "\r\n\r\nData",
43451 "HTTP/1.1 304 NOT MODIFIED\r\nETag: \"123456\"\r\n\r\n",
43452 ),
43453 );
43454 }
43455
43456 /**
43457 * @dataProvider cacheRevalidationDataProvider
43458 */
43459 public function testRevalidatesResponsesAgainstOriginServer($can, $request, $response, $validate = null, $result = null)
43460 {
43461 // Send some responses to the test server for cache validation
43462 $server = $this->getServer();
43463 $server->flush();
43464
43465 if ($validate) {
43466 $server->enqueue($validate);
43467 }
43468
43469 $request = RequestFactory::getInstance()->fromMessage("GET / HTTP/1.1\r\nHost: 127.0.0.1:" . $server->getPort() . "\r\n" . $request);
43470 $response = Response::fromMessage($response);
43471 $request->setClient(new Client());
43472
43473 $plugin = new CachePlugin(new DoctrineCacheAdapter(new ArrayCache()));
43474 $this->assertEquals(
43475 $can,
43476 $plugin->canResponseSatisfyRequest($request, $response),
43477 '-> ' . $request . "\n" . $response
43478 );
43479
43480 if ($result) {
43481 $result = Response::fromMessage($result);
43482 $result->removeHeader('Date');
43483 $request->getResponse()->removeHeader('Date');
43484 $request->getResponse()->removeHeader('Connection');
43485 // Get rid of dates
43486 $this->assertEquals((string) $result, (string) $request->getResponse());
43487 }
43488
43489 if ($validate) {
43490 $this->assertEquals(1, count($server->getReceivedRequests()));
43491 }
43492 }
43493
43494 public function testHandles404RevalidationResponses()
43495 {
43496 $request = new Request('GET', 'http://foo.com');
43497 $request->setClient(new Client());
43498 $badResponse = new Response(404, array(), 'Oh no!');
43499 $badRequest = clone $request;
43500 $badRequest->setResponse($badResponse, true);
43501 $response = new Response(200, array(), 'foo');
43502
43503 // Seed the cache
43504 $s = new DefaultCacheStorage(new DoctrineCacheAdapter(new ArrayCache()));
43505 $s->cache($request, $response);
43506 $this->assertNotNull($s->fetch($request));
43507
43508 $rev = $this->getMockBuilder('Guzzle\Plugin\Cache\DefaultRevalidation')
43509 ->setConstructorArgs(array($s))
43510 ->setMethods(array('createRevalidationRequest'))
43511 ->getMock();
43512
43513 $rev->expects($this->once())
43514 ->method('createRevalidationRequest')
43515 ->will($this->returnValue($badRequest));
43516
43517 try {
43518 $rev->revalidate($request, $response);
43519 $this->fail('Should have thrown an exception');
43520 } catch (BadResponseException $e) {
43521 $this->assertSame($badResponse, $e->getResponse());
43522 $this->assertNull($s->fetch($request));
43523 }
43524 }
43525
43526 public function testCanRevalidateWithPlugin()
43527 {
43528 $this->getServer()->flush();
43529 $this->getServer()->enqueue(array(
43530 "HTTP/1.1 200 OK\r\n" .
43531 "Date: Mon, 12 Nov 2012 03:06:37 GMT\r\n" .
43532 "Cache-Control: private, s-maxage=0, max-age=0, must-revalidate\r\n" .
43533 "Last-Modified: Mon, 12 Nov 2012 02:53:38 GMT\r\n" .
43534 "Content-Length: 2\r\n\r\nhi",
43535 "HTTP/1.0 304 Not Modified\r\n" .
43536 "Date: Mon, 12 Nov 2012 03:06:38 GMT\r\n" .
43537 "Content-Type: text/html; charset=UTF-8\r\n" .
43538 "Last-Modified: Mon, 12 Nov 2012 02:53:38 GMT\r\n" .
43539 "Age: 6302\r\n\r\n",
43540 "HTTP/1.0 304 Not Modified\r\n" .
43541 "Date: Mon, 12 Nov 2012 03:06:38 GMT\r\n" .
43542 "Content-Type: text/html; charset=UTF-8\r\n" .
43543 "Last-Modified: Mon, 12 Nov 2012 02:53:38 GMT\r\n" .
43544 "Age: 6302\r\n\r\n",
43545 ));
43546 $client = new Client($this->getServer()->getUrl());
43547 $client->addSubscriber(new CachePlugin());
43548 $this->assertEquals(200, $client->get()->send()->getStatusCode());
43549 $this->assertEquals(200, $client->get()->send()->getStatusCode());
43550 $this->assertEquals(200, $client->get()->send()->getStatusCode());
43551 $this->assertEquals(3, count($this->getServer()->getReceivedRequests()));
43552 }
43553
43554 public function testCanHandleRevalidationFailures()
43555 {
43556 $client = new Client($this->getServer()->getUrl());
43557 $lm = gmdate('c', time() - 60);
43558 $mock = new MockPlugin(array(
43559 new Response(200, array(
43560 'Date' => $lm,
43561 'Cache-Control' => 'max-age=100, must-revalidate, stale-if-error=9999',
43562 'Last-Modified' => $lm,
43563 'Content-Length' => 2
43564 ), 'hi'),
43565 new CurlException('Bleh')
43566 ));
43567 $client->addSubscriber(new CachePlugin());
43568 $client->addSubscriber($mock);
43569 $client->get()->send();
43570 $response = $client->get()->send();
43571 $this->assertEquals(200, $response->getStatusCode());
43572 $this->assertEquals('hi', $response->getBody(true));
43573 $this->assertEquals(2, count($mock->getReceivedRequests()));
43574 $this->assertEquals(0, count($mock->getQueue()));
43575 }
43576
43577 public function testCanHandleStaleIfErrorWhenRevalidating()
43578 {
43579 $lm = gmdate('c', time() - 60);
43580 $mock = new MockPlugin(array(
43581 new Response(200, array(
43582 'Date' => $lm,
43583 'Cache-Control' => 'must-revalidate, max-age=0, stale-if-error=1200',
43584 'Last-Modified' => $lm,
43585 'Content-Length' => 2
43586 ), 'hi'),
43587 new CurlException('Oh no!')
43588 ));
43589 $cache = new CachePlugin();
43590 $client = new Client('http://www.example.com');
43591 $client->addSubscriber($cache);
43592 $client->addSubscriber($mock);
43593 $this->assertEquals(200, $client->get()->send()->getStatusCode());
43594 $response = $client->get()->send();
43595 $this->assertEquals(200, $response->getStatusCode());
43596 $this->assertCount(0, $mock);
43597 $this->assertEquals('HIT from GuzzleCache', (string) $response->getHeader('X-Cache-Lookup'));
43598 $this->assertEquals('HIT_ERROR from GuzzleCache', (string) $response->getHeader('X-Cache'));
43599 }
43600 }
43601 <?php
43602
43603 namespace Guzzle\Tests\Plugin\Log;
43604
43605 use Guzzle\Log\ClosureLogAdapter;
43606 use Guzzle\Http\Client;
43607 use Guzzle\Http\EntityBody;
43608 use Guzzle\Http\Message\Request;
43609 use Guzzle\Http\Message\Response;
43610 use Guzzle\Plugin\Log\LogPlugin;
43611 use Guzzle\Common\Event;
43612
43613 /**
43614 * @group server
43615 * @covers Guzzle\Plugin\Log\LogPlugin
43616 */
43617 class LogPluginTest extends \Guzzle\Tests\GuzzleTestCase
43618 {
43619 protected $adapter;
43620
43621 public function setUp()
43622 {
43623 $this->adapter = new ClosureLogAdapter(function ($message) {
43624 echo $message;
43625 });
43626 }
43627
43628 public function testIgnoresCurlEventsWhenNotWiringBodies()
43629 {
43630 $p = new LogPlugin($this->adapter);
43631 $this->assertNotEmpty($p->getSubscribedEvents());
43632 $event = new Event(array('request' => new Request('GET', 'http://foo.com')));
43633 $p->onCurlRead($event);
43634 $p->onCurlWrite($event);
43635 $p->onRequestBeforeSend($event);
43636 }
43637
43638 public function testLogsWhenComplete()
43639 {
43640 $output = '';
43641 $p = new LogPlugin(new ClosureLogAdapter(function ($message) use (&$output) {
43642 $output = $message;
43643 }), '{method} {resource} | {code} {res_body}');
43644
43645 $p->onRequestSent(new Event(array(
43646 'request' => new Request('GET', 'http://foo.com'),
43647 'response' => new Response(200, array(), 'Foo')
43648 )));
43649
43650 $this->assertEquals('GET / | 200 Foo', $output);
43651 }
43652
43653 public function testWiresBodiesWhenNeeded()
43654 {
43655 $client = new Client($this->getServer()->getUrl());
43656 $plugin = new LogPlugin($this->adapter, '{req_body} | {res_body}', true);
43657 $client->getEventDispatcher()->addSubscriber($plugin);
43658 $request = $client->put();
43659
43660 // Send the response from the dummy server as the request body
43661 $this->getServer()->enqueue("HTTP/1.1 200 OK\r\nContent-Length: 4\r\n\r\nsend");
43662 $stream = fopen($this->getServer()->getUrl(), 'r');
43663 $request->setBody(EntityBody::factory($stream, 4));
43664
43665 $tmpFile = tempnam(sys_get_temp_dir(), 'non_repeatable');
43666 $request->setResponseBody(EntityBody::factory(fopen($tmpFile, 'w')));
43667
43668 $this->getServer()->enqueue("HTTP/1.1 200 OK\r\nContent-Length: 8\r\n\r\nresponse");
43669
43670 ob_start();
43671 $request->send();
43672 $message = ob_get_clean();
43673
43674 unlink($tmpFile);
43675 $this->assertContains("send", $message);
43676 $this->assertContains("response", $message);
43677 }
43678
43679 public function testHasHelpfulStaticFactoryMethod()
43680 {
43681 $s = fopen('php://temp', 'r+');
43682 $client = new Client();
43683 $client->addSubscriber(LogPlugin::getDebugPlugin(true, $s));
43684 $request = $client->put('http://foo.com', array('Content-Type' => 'Foo'), 'Bar');
43685 $request->setresponse(new Response(200), true);
43686 $request->send();
43687 rewind($s);
43688 $contents = stream_get_contents($s);
43689 $this->assertContains('# Request:', $contents);
43690 $this->assertContainsIns('PUT / HTTP/1.1', $contents);
43691 $this->assertContains('# Response:', $contents);
43692 $this->assertContainsIns('HTTP/1.1 200 OK', $contents);
43693 $this->assertContains('# Errors:', $contents);
43694 }
43695 }
43696 <?php
43697
43698 namespace Guzzle\Tests\Plugin\Backoff;
43699
43700 use Guzzle\Common\Event;
43701 use Guzzle\Http\Exception\CurlException;
43702 use Guzzle\Http\Client;
43703 use Guzzle\Plugin\Backoff\BackoffPlugin;
43704 use Guzzle\Http\Message\RequestInterface;
43705 use Guzzle\Http\Message\Request;
43706 use Guzzle\Http\Message\EntityEnclosingRequest;
43707 use Guzzle\Http\Message\Response;
43708 use Guzzle\Http\Curl\CurlMulti;
43709 use Guzzle\Http\Curl\CurlMultiInterface;
43710 use Guzzle\Plugin\Backoff\ConstantBackoffStrategy;
43711 use Guzzle\Plugin\Backoff\CurlBackoffStrategy;
43712 use Guzzle\Plugin\Backoff\HttpBackoffStrategy;
43713 use Guzzle\Plugin\Backoff\TruncatedBackoffStrategy;
43714 use Symfony\Component\EventDispatcher\EventSubscriberInterface;
43715
43716 /**
43717 * @group server
43718 * @covers Guzzle\Plugin\Backoff\BackoffPlugin
43719 */
43720 class BackoffPluginTest extends \Guzzle\Tests\GuzzleTestCase implements EventSubscriberInterface
43721 {
43722 protected $retried;
43723
43724 public function setUp()
43725 {
43726 $this->retried = false;
43727 }
43728
43729 public static function getSubscribedEvents()
43730 {
43731 return array(BackoffPlugin::RETRY_EVENT => 'onRequestRetry');
43732 }
43733
43734 public function onRequestRetry(Event $event)
43735 {
43736 $this->retried = $event;
43737 }
43738
43739 public function testHasEventList()
43740 {
43741 $this->assertEquals(1, count(BackoffPlugin::getAllEvents()));
43742 }
43743
43744 public function testCreatesDefaultExponentialBackoffPlugin()
43745 {
43746 $plugin = BackoffPlugin::getExponentialBackoff(3, array(204), array(10));
43747 $this->assertInstanceOf('Guzzle\Plugin\Backoff\BackoffPlugin', $plugin);
43748 $strategy = $this->readAttribute($plugin, 'strategy');
43749 $this->assertInstanceOf('Guzzle\Plugin\Backoff\TruncatedBackoffStrategy', $strategy);
43750 $this->assertEquals(3, $this->readAttribute($strategy, 'max'));
43751 $strategy = $this->readAttribute($strategy, 'next');
43752 $this->assertInstanceOf('Guzzle\Plugin\Backoff\HttpBackoffStrategy', $strategy);
43753 $this->assertEquals(array(204 => true), $this->readAttribute($strategy, 'errorCodes'));
43754 $strategy = $this->readAttribute($strategy, 'next');
43755 $this->assertInstanceOf('Guzzle\Plugin\Backoff\CurlBackoffStrategy', $strategy);
43756 $this->assertEquals(array(10 => true), $this->readAttribute($strategy, 'errorCodes'));
43757 $strategy = $this->readAttribute($strategy, 'next');
43758 $this->assertInstanceOf('Guzzle\Plugin\Backoff\ExponentialBackoffStrategy', $strategy);
43759 }
43760
43761 public function testDoesNotRetryUnlessStrategyReturnsNumber()
43762 {
43763 $request = new Request('GET', 'http://www.example.com');
43764 $request->setState('transfer');
43765
43766 $mock = $this->getMockBuilder('Guzzle\Plugin\Backoff\BackoffStrategyInterface')
43767 ->setMethods(array('getBackoffPeriod'))
43768 ->getMockForAbstractClass();
43769
43770 $mock->expects($this->once())
43771 ->method('getBackoffPeriod')
43772 ->will($this->returnValue(false));
43773
43774 $plugin = new BackoffPlugin($mock);
43775 $plugin->addSubscriber($this);
43776 $plugin->onRequestSent(new Event(array('request' => $request)));
43777 $this->assertFalse($this->retried);
43778 }
43779
43780 public function testUpdatesRequestForRetry()
43781 {
43782 $request = new Request('GET', 'http://www.example.com');
43783 $request->setState('transfer');
43784 $response = new Response(500);
43785 $handle = $this->getMockBuilder('Guzzle\Http\Curl\CurlHandle')->disableOriginalConstructor()->getMock();
43786 $e = new CurlException();
43787 $e->setCurlHandle($handle);
43788
43789 $plugin = new BackoffPlugin(new ConstantBackoffStrategy(10));
43790 $plugin->addSubscriber($this);
43791
43792 $event = new Event(array(
43793 'request' => $request,
43794 'response' => $response,
43795 'exception' => $e
43796 ));
43797
43798 $plugin->onRequestSent($event);
43799 $this->assertEquals(array(
43800 'request' => $request,
43801 'response' => $response,
43802 'handle' => $handle,
43803 'retries' => 1,
43804 'delay' => 10
43805 ), $this->readAttribute($this->retried, 'context'));
43806
43807 $plugin->onRequestSent($event);
43808 $this->assertEquals(array(
43809 'request' => $request,
43810 'response' => $response,
43811 'handle' => $handle,
43812 'retries' => 2,
43813 'delay' => 10
43814 ), $this->readAttribute($this->retried, 'context'));
43815 }
43816
43817 public function testDoesNothingWhenNotRetryingAndPollingRequest()
43818 {
43819 $request = new Request('GET', 'http://www.foo.com');
43820 $plugin = new BackoffPlugin(new ConstantBackoffStrategy(10));
43821 $plugin->onRequestPoll(new Event(array('request' => $request)));
43822 }
43823
43824 public function testRetriesRequests()
43825 {
43826 // Create a script to return several 500 and 503 response codes
43827 $this->getServer()->flush();
43828 $this->getServer()->enqueue(array(
43829 "HTTP/1.1 500 Internal Server Error\r\nContent-Length: 0\r\n\r\n",
43830 "HTTP/1.1 500 Internal Server Error\r\nContent-Length: 0\r\n\r\n",
43831 "HTTP/1.1 200 OK\r\nContent-Length: 4\r\n\r\ndata"
43832 ));
43833
43834 $plugin = new BackoffPlugin(
43835 new TruncatedBackoffStrategy(3,
43836 new HttpBackoffStrategy(null,
43837 new CurlBackoffStrategy(null,
43838 new ConstantBackoffStrategy(0.05)
43839 )
43840 )
43841 )
43842 );
43843
43844 $client = new Client($this->getServer()->getUrl());
43845 $client->getEventDispatcher()->addSubscriber($plugin);
43846 $request = $client->get();
43847 $request->send();
43848
43849 // Make sure it eventually completed successfully
43850 $this->assertEquals(200, $request->getResponse()->getStatusCode());
43851 $this->assertEquals('data', $request->getResponse()->getBody(true));
43852
43853 // Check that three requests were made to retry this request
43854 $this->assertEquals(3, count($this->getServer()->getReceivedRequests(false)));
43855 $this->assertEquals(2, $request->getParams()->get(BackoffPlugin::RETRY_PARAM));
43856 }
43857
43858 /**
43859 * @expectedException \Guzzle\Http\Exception\ServerErrorResponseException
43860 */
43861 public function testFailsOnTruncation()
43862 {
43863 $this->getServer()->flush();
43864 $this->getServer()->enqueue(array(
43865 "HTTP/1.1 500 Internal Server Error\r\nContent-Length: 0\r\n\r\n",
43866 "HTTP/1.1 500 Internal Server Error\r\nContent-Length: 0\r\n\r\n"
43867 ));
43868
43869 $plugin = new BackoffPlugin(
43870 new TruncatedBackoffStrategy(2,
43871 new HttpBackoffStrategy(null,
43872 new ConstantBackoffStrategy(0.05)
43873 )
43874 )
43875 );
43876
43877 $client = new Client($this->getServer()->getUrl());
43878 $client->addSubscriber($plugin);
43879 $client->get()->send();
43880 }
43881
43882 public function testRetriesRequestsWhenInParallel()
43883 {
43884 // Create a script to return several 500 and 503 response codes
43885 $this->getServer()->flush();
43886 $this->getServer()->enqueue(array(
43887 "HTTP/1.1 500 Internal Server Error\r\nContent-Length: 0\r\n\r\n",
43888 "HTTP/1.1 500 Internal Server Error\r\nContent-Length: 0\r\n\r\n",
43889 "HTTP/1.1 500 Internal Server Error\r\nContent-Length: 0\r\n\r\n",
43890 "HTTP/1.1 500 Internal Server Error\r\nContent-Length: 0\r\n\r\n",
43891 "HTTP/1.1 500 Internal Server Error\r\nContent-Length: 0\r\n\r\n",
43892 "HTTP/1.1 500 Internal Server Error\r\nContent-Length: 0\r\n\r\n",
43893 "HTTP/1.1 500 Internal Server Error\r\nContent-Length: 0\r\n\r\n",
43894 "HTTP/1.1 500 Internal Server Error\r\nContent-Length: 0\r\n\r\n",
43895 "HTTP/1.1 500 Internal Server Error\r\nContent-Length: 0\r\n\r\n",
43896 "HTTP/1.1 500 Internal Server Error\r\nContent-Length: 0\r\n\r\n",
43897 "HTTP/1.1 200 OK\r\nContent-Length: 4\r\n\r\ndata",
43898 "HTTP/1.1 200 OK\r\nContent-Length: 4\r\n\r\ndata",
43899 "HTTP/1.1 200 OK\r\nContent-Length: 4\r\n\r\ndata",
43900 "HTTP/1.1 200 OK\r\nContent-Length: 4\r\n\r\ndata",
43901 "HTTP/1.1 200 OK\r\nContent-Length: 4\r\n\r\ndata"
43902 ));
43903
43904 $plugin = new BackoffPlugin(
43905 new HttpBackoffStrategy(null,
43906 new TruncatedBackoffStrategy(3,
43907 new CurlBackoffStrategy(null,
43908 new ConstantBackoffStrategy(0.1)
43909 )
43910 )
43911 )
43912 );
43913 $client = new Client($this->getServer()->getUrl());
43914 $client->getEventDispatcher()->addSubscriber($plugin);
43915 $requests = array();
43916 for ($i = 0; $i < 5; $i++) {
43917 $requests[] = $client->get();
43918 }
43919 $client->send($requests);
43920
43921 $this->assertEquals(15, count($this->getServer()->getReceivedRequests(false)));
43922 }
43923
43924 /**
43925 * @covers Guzzle\Plugin\Backoff\BackoffPlugin
43926 * @covers Guzzle\Http\Curl\CurlMulti
43927 */
43928 public function testRetriesPooledRequestsUsingDelayAndPollingEvent()
43929 {
43930 $this->getServer()->flush();
43931 $this->getServer()->enqueue(array(
43932 "HTTP/1.1 500 Internal Server Error\r\nContent-Length: 0\r\n\r\n",
43933 "HTTP/1.1 200 OK\r\nContent-Length: 4\r\n\r\ndata"
43934 ));
43935 // Need to sleep for some time ensure that the polling works correctly in the observer
43936 $plugin = new BackoffPlugin(new HttpBackoffStrategy(null,
43937 new TruncatedBackoffStrategy(1,
43938 new ConstantBackoffStrategy(0.5))));
43939
43940 $client = new Client($this->getServer()->getUrl());
43941 $client->getEventDispatcher()->addSubscriber($plugin);
43942 $request = $client->get();
43943 $request->send();
43944 // Make sure it eventually completed successfully
43945 $this->assertEquals('data', $request->getResponse()->getBody(true));
43946 // Check that two requests were made to retry this request
43947 $this->assertEquals(2, count($this->getServer()->getReceivedRequests(false)));
43948 }
43949
43950 public function testSeeksToBeginningOfRequestBodyWhenRetrying()
43951 {
43952 // Create a request with a body
43953 $request = new EntityEnclosingRequest('PUT', 'http://www.example.com');
43954 $request->setBody('abc');
43955 // Set the retry time to be something that will be retried always
43956 $request->getParams()->set(BackoffPlugin::DELAY_PARAM, 2);
43957 // Seek to the end of the stream
43958 $request->getBody()->seek(3);
43959 $this->assertEquals('', $request->getBody()->read(1));
43960 // Create a plugin that does not delay when retrying
43961 $plugin = new BackoffPlugin(new ConstantBackoffStrategy(0));
43962 $plugin->onRequestPoll($this->getMockEvent($request));
43963 // Ensure that the stream was seeked to 0
43964 $this->assertEquals('a', $request->getBody()->read(1));
43965 }
43966
43967 public function testDoesNotSeekOnRequestsWithNoBodyWhenRetrying()
43968 {
43969 // Create a request with a body
43970 $request = new EntityEnclosingRequest('PUT', 'http://www.example.com');
43971 $request->getParams()->set(BackoffPlugin::DELAY_PARAM, 2);
43972 $plugin = new BackoffPlugin(new ConstantBackoffStrategy(0));
43973 $plugin->onRequestPoll($this->getMockEvent($request));
43974 }
43975
43976 protected function getMockEvent(RequestInterface $request)
43977 {
43978 // Create a mock curl multi object
43979 $multi = $this->getMockBuilder('Guzzle\Http\Curl\CurlMulti')
43980 ->setMethods(array('remove', 'add'))
43981 ->getMock();
43982
43983 // Create an event that is expected for the Poll event
43984 $event = new Event(array(
43985 'request' => $request,
43986 'curl_multi' => $multi
43987 ));
43988 $event->setName(CurlMultiInterface::POLLING_REQUEST);
43989
43990 return $event;
43991 }
43992 }
43993 <?php
43994
43995 namespace Guzzle\Tests\Plugin\Backoff;
43996
43997 use Guzzle\Plugin\Backoff\ReasonPhraseBackoffStrategy;
43998 use Guzzle\Http\Message\Response;
43999
44000 /**
44001 * @covers Guzzle\Plugin\Backoff\ReasonPhraseBackoffStrategy
44002 * @covers Guzzle\Plugin\Backoff\AbstractErrorCodeBackoffStrategy
44003 */
44004 class ReasonPhraseBackoffStrategyTest extends \Guzzle\Tests\GuzzleTestCase
44005 {
44006 public function testRetriesWhenCodeMatches()
44007 {
44008 $this->assertEmpty(ReasonPhraseBackoffStrategy::getDefaultFailureCodes());
44009 $strategy = new ReasonPhraseBackoffStrategy(array('Foo', 'Internal Server Error'));
44010 $this->assertTrue($strategy->makesDecision());
44011 $request = $this->getMock('Guzzle\Http\Message\Request', array(), array(), '', false);
44012 $response = new Response(200);
44013 $this->assertEquals(false, $strategy->getBackoffPeriod(0, $request, $response));
44014 $response->setStatus(200, 'Foo');
44015 $this->assertEquals(0, $strategy->getBackoffPeriod(0, $request, $response));
44016 }
44017
44018 public function testIgnoresNonErrors()
44019 {
44020 $strategy = new ReasonPhraseBackoffStrategy();
44021 $request = $this->getMock('Guzzle\Http\Message\Request', array(), array(), '', false);
44022 $this->assertEquals(false, $strategy->getBackoffPeriod(0, $request));
44023 }
44024 }
44025 <?php
44026
44027 namespace Guzzle\Tests\Plugin\Backoff;
44028
44029 use Guzzle\Plugin\Backoff\LinearBackoffStrategy;
44030
44031 /**
44032 * @covers Guzzle\Plugin\Backoff\LinearBackoffStrategy
44033 */
44034 class LinearBackoffStrategyTest extends \Guzzle\Tests\GuzzleTestCase
44035 {
44036 public function testRetriesWithLinearDelay()
44037 {
44038 $strategy = new LinearBackoffStrategy(5);
44039 $this->assertFalse($strategy->makesDecision());
44040 $request = $this->getMock('Guzzle\Http\Message\Request', array(), array(), '', false);
44041 $this->assertEquals(0, $strategy->getBackoffPeriod(0, $request));
44042 $this->assertEquals(5, $strategy->getBackoffPeriod(1, $request));
44043 $this->assertEquals(10, $strategy->getBackoffPeriod(2, $request));
44044 }
44045 }
44046 <?php
44047
44048 namespace Guzzle\Tests\Plugin\Backoff;
44049
44050 use Guzzle\Common\Event;
44051 use Guzzle\Log\ClosureLogAdapter;
44052 use Guzzle\Http\Curl\CurlHandle;
44053 use Guzzle\Plugin\Backoff\BackoffLogger;
44054 use Guzzle\Http\Message\Response;
44055 use Guzzle\Http\Message\RequestFactory;
44056
44057 /**
44058 * @covers Guzzle\Plugin\Backoff\BackoffLogger
44059 */
44060 class BackoffLoggerTest extends \Guzzle\Tests\GuzzleTestCase
44061 {
44062 public $message;
44063
44064 public function setUp()
44065 {
44066 $this->message = '';
44067 }
44068
44069 public function testHasEventList()
44070 {
44071 $this->assertEquals(1, count(BackoffLogger::getSubscribedEvents()));
44072 }
44073
44074 public function testLogsEvents()
44075 {
44076 list($logPlugin, $request, $response) = $this->getMocks();
44077
44078 $response = $this->getMockBuilder('Guzzle\Http\Message\Response')
44079 ->setConstructorArgs(array(503))
44080 ->setMethods(array('getInfo'))
44081 ->getMock();
44082
44083 $response->expects($this->any())
44084 ->method('getInfo')
44085 ->will($this->returnValue(2));
44086
44087 $handle = $this->getMockHandle();
44088
44089 $event = new Event(array(
44090 'request' => $request,
44091 'response' => $response,
44092 'retries' => 1,
44093 'delay' => 3,
44094 'handle' => $handle
44095 ));
44096
44097 $logPlugin->onRequestRetry($event);
44098 $this->assertContains(
44099 '] PUT http://www.example.com - 503 Service Unavailable - Retries: 1, Delay: 3, Time: 2, 2, cURL: 30 Foo',
44100 $this->message
44101 );
44102 }
44103
44104 public function testCanSetTemplate()
44105 {
44106 $l = new BackoffLogger(new ClosureLogAdapter(function () {}));
44107 $l->setTemplate('foo');
44108 $t = $this->readAttribute($l, 'formatter');
44109 $this->assertEquals('foo', $this->readAttribute($t, 'template'));
44110 }
44111
44112 /**
44113 * @return array
44114 */
44115 protected function getMocks()
44116 {
44117 $that = $this;
44118 $logger = new ClosureLogAdapter(function ($message) use ($that) {
44119 $that->message .= $message . "\n";
44120 });
44121 $logPlugin = new BackoffLogger($logger);
44122 $response = new Response(503);
44123 $request = RequestFactory::getInstance()->create('PUT', 'http://www.example.com', array(
44124 'Content-Length' => 3,
44125 'Foo' => 'Bar'
44126 ));
44127
44128 return array($logPlugin, $request, $response);
44129 }
44130
44131 /**
44132 * @return CurlHandle
44133 */
44134 protected function getMockHandle()
44135 {
44136 $handle = $this->getMockBuilder('Guzzle\Http\Curl\CurlHandle')
44137 ->disableOriginalConstructor()
44138 ->setMethods(array('getError', 'getErrorNo', 'getInfo'))
44139 ->getMock();
44140
44141 $handle->expects($this->once())
44142 ->method('getError')
44143 ->will($this->returnValue('Foo'));
44144
44145 $handle->expects($this->once())
44146 ->method('getErrorNo')
44147 ->will($this->returnValue(30));
44148
44149 $handle->expects($this->any())
44150 ->method('getInfo')
44151 ->will($this->returnValue(2));
44152
44153 return $handle;
44154 }
44155 }
44156 <?php
44157
44158 namespace Guzzle\Tests\Plugin\Backoff;
44159
44160 use Guzzle\Plugin\Backoff\CallbackBackoffStrategy;
44161
44162 /**
44163 * @covers Guzzle\Plugin\Backoff\CallbackBackoffStrategy
44164 */
44165 class CallbackBackoffStrategyTest extends \Guzzle\Tests\GuzzleTestCase
44166 {
44167 /**
44168 * @expectedException Guzzle\Common\Exception\InvalidArgumentException
44169 */
44170 public function testEnsuresIsCallable()
44171 {
44172 $strategy = new CallbackBackoffStrategy(new \stdClass(), true);
44173 }
44174
44175 public function testRetriesWithCallable()
44176 {
44177 $request = $this->getMock('Guzzle\Http\Message\Request', array(), array(), '', false);
44178 $strategy = new CallbackBackoffStrategy(function () { return 10; }, true);
44179 $this->assertTrue($strategy->makesDecision());
44180 $this->assertEquals(10, $strategy->getBackoffPeriod(0, $request));
44181 // Ensure it chains correctly when null is returned
44182 $strategy = new CallbackBackoffStrategy(function () { return null; }, false);
44183 $this->assertFalse($strategy->makesDecision());
44184 $this->assertFalse($strategy->getBackoffPeriod(0, $request));
44185 }
44186 }
44187 <?php
44188
44189 namespace Guzzle\Tests\Plugin\Backoff;
44190
44191 use Guzzle\Plugin\Backoff\ConstantBackoffStrategy;
44192
44193 /**
44194 * @covers Guzzle\Plugin\Backoff\ConstantBackoffStrategy
44195 */
44196 class ConstantBackoffStrategyTest extends \Guzzle\Tests\GuzzleTestCase
44197 {
44198 public function testRetriesWithConstantDelay()
44199 {
44200 $strategy = new ConstantBackoffStrategy(3.5);
44201 $this->assertFalse($strategy->makesDecision());
44202 $request = $this->getMock('Guzzle\Http\Message\Request', array(), array(), '', false);
44203 $this->assertEquals(3.5, $strategy->getBackoffPeriod(0, $request));
44204 $this->assertEquals(3.5, $strategy->getBackoffPeriod(1, $request));
44205 }
44206 }
44207 <?php
44208
44209 namespace Guzzle\Tests\Plugin\Backoff;
44210
44211 use Guzzle\Plugin\Backoff\HttpBackoffStrategy;
44212 use Guzzle\Http\Message\Response;
44213
44214 /**
44215 * @covers Guzzle\Plugin\Backoff\HttpBackoffStrategy
44216 * @covers Guzzle\Plugin\Backoff\AbstractErrorCodeBackoffStrategy
44217 */
44218 class HttpBackoffStrategyTest extends \Guzzle\Tests\GuzzleTestCase
44219 {
44220 public function testRetriesWhenCodeMatches()
44221 {
44222 $this->assertNotEmpty(HttpBackoffStrategy::getDefaultFailureCodes());
44223 $strategy = new HttpBackoffStrategy();
44224 $this->assertTrue($strategy->makesDecision());
44225 $request = $this->getMock('Guzzle\Http\Message\Request', array(), array(), '', false);
44226
44227 $response = new Response(200);
44228 $this->assertEquals(false, $strategy->getBackoffPeriod(0, $request, $response));
44229 $response->setStatus(400);
44230 $this->assertEquals(false, $strategy->getBackoffPeriod(0, $request, $response));
44231
44232 foreach (HttpBackoffStrategy::getDefaultFailureCodes() as $code) {
44233 $this->assertEquals(0, $strategy->getBackoffPeriod(0, $request, $response->setStatus($code)));
44234 }
44235 }
44236
44237 public function testAllowsCustomCodes()
44238 {
44239 $strategy = new HttpBackoffStrategy(array(204));
44240 $request = $this->getMock('Guzzle\Http\Message\Request', array(), array(), '', false);
44241 $response = new Response(204);
44242 $this->assertEquals(0, $strategy->getBackoffPeriod(0, $request, $response));
44243 $response->setStatus(500);
44244 $this->assertEquals(false, $strategy->getBackoffPeriod(0, $request, $response));
44245 }
44246
44247 public function testIgnoresNonErrors()
44248 {
44249 $strategy = new HttpBackoffStrategy();
44250 $request = $this->getMock('Guzzle\Http\Message\Request', array(), array(), '', false);
44251 $this->assertEquals(false, $strategy->getBackoffPeriod(0, $request));
44252 }
44253 }
44254 <?php
44255
44256 namespace Guzzle\Tests\Plugin\Backoff;
44257
44258 use Guzzle\Plugin\Backoff\ExponentialBackoffStrategy;
44259
44260 /**
44261 * @covers Guzzle\Plugin\Backoff\ExponentialBackoffStrategy
44262 */
44263 class ExponentialBackoffStrategyTest extends \Guzzle\Tests\GuzzleTestCase
44264 {
44265 public function testRetriesWithExponentialDelay()
44266 {
44267 $strategy = new ExponentialBackoffStrategy();
44268 $this->assertFalse($strategy->makesDecision());
44269 $request = $this->getMock('Guzzle\Http\Message\Request', array(), array(), '', false);
44270 $this->assertEquals(1, $strategy->getBackoffPeriod(0, $request));
44271 $this->assertEquals(2, $strategy->getBackoffPeriod(1, $request));
44272 $this->assertEquals(4, $strategy->getBackoffPeriod(2, $request));
44273 $this->assertEquals(8, $strategy->getBackoffPeriod(3, $request));
44274 $this->assertEquals(16, $strategy->getBackoffPeriod(4, $request));
44275 }
44276 }
44277 <?php
44278
44279 namespace Guzzle\Tests\Plugin\Backoff;
44280
44281 use Guzzle\Http\Message\Response;
44282 use Guzzle\Plugin\Backoff\CurlBackoffStrategy;
44283 use Guzzle\Http\Exception\CurlException;
44284
44285 /**
44286 * @covers Guzzle\Plugin\Backoff\CurlBackoffStrategy
44287 * @covers Guzzle\Plugin\Backoff\AbstractErrorCodeBackoffStrategy
44288 */
44289 class CurlBackoffStrategyTest extends \Guzzle\Tests\GuzzleTestCase
44290 {
44291 public function testRetriesWithExponentialDelay()
44292 {
44293 $this->assertNotEmpty(CurlBackoffStrategy::getDefaultFailureCodes());
44294 $strategy = new CurlBackoffStrategy();
44295 $this->assertTrue($strategy->makesDecision());
44296 $request = $this->getMock('Guzzle\Http\Message\Request', array(), array(), '', false);
44297 $e = new CurlException();
44298 $e->setError('foo', CURLE_BAD_CALLING_ORDER);
44299 $this->assertEquals(false, $strategy->getBackoffPeriod(0, $request, null, $e));
44300
44301 foreach (CurlBackoffStrategy::getDefaultFailureCodes() as $code) {
44302 $this->assertEquals(0, $strategy->getBackoffPeriod(0, $request, null, $e->setError('foo', $code)));
44303 }
44304 }
44305
44306 public function testIgnoresNonErrors()
44307 {
44308 $strategy = new CurlBackoffStrategy();
44309 $request = $this->getMock('Guzzle\Http\Message\Request', array(), array(), '', false);
44310 $this->assertEquals(false, $strategy->getBackoffPeriod(0, $request, new Response(200)));
44311 }
44312 }
44313 <?php
44314
44315 namespace Guzzle\Tests\Plugin\Backoff;
44316
44317 use Guzzle\Http\Message\Request;
44318 use Guzzle\Plugin\Backoff\TruncatedBackoffStrategy;
44319 use Guzzle\Plugin\Backoff\CallbackBackoffStrategy;
44320
44321 /**
44322 * @covers Guzzle\Plugin\Backoff\AbstractBackoffStrategy
44323 */
44324 class AbstractBackoffStrategyTest extends \Guzzle\Tests\GuzzleTestCase
44325 {
44326 protected function getMockStrategy()
44327 {
44328 return $this->getMockBuilder('Guzzle\Plugin\Backoff\AbstractBackoffStrategy')
44329 ->setMethods(array('getDelay', 'makesDecision'))
44330 ->getMockForAbstractClass();
44331 }
44332
44333 public function testReturnsZeroWhenNoNextAndGotNull()
44334 {
44335 $request = new Request('GET', 'http://www.foo.com');
44336 $mock = $this->getMockStrategy();
44337 $mock->expects($this->atLeastOnce())->method('getDelay')->will($this->returnValue(null));
44338 $this->assertEquals(0, $mock->getBackoffPeriod(0, $request));
44339 }
44340
44341 public function testReturnsFalse()
44342 {
44343 $request = new Request('GET', 'http://www.foo.com');
44344 $mock = $this->getMockStrategy();
44345 $mock->expects($this->atLeastOnce())->method('getDelay')->will($this->returnValue(false));
44346 $this->assertEquals(false, $mock->getBackoffPeriod(0, $request));
44347 }
44348
44349 public function testReturnsNextValueWhenNullOrTrue()
44350 {
44351 $request = new Request('GET', 'http://www.foo.com');
44352 $mock = $this->getMockStrategy();
44353 $mock->expects($this->atLeastOnce())->method('getDelay')->will($this->returnValue(null));
44354 $mock->expects($this->any())->method('makesDecision')->will($this->returnValue(false));
44355
44356 $mock2 = $this->getMockStrategy();
44357 $mock2->expects($this->atLeastOnce())->method('getDelay')->will($this->returnValue(10));
44358 $mock2->expects($this->atLeastOnce())->method('makesDecision')->will($this->returnValue(true));
44359 $mock->setNext($mock2);
44360
44361 $this->assertEquals(10, $mock->getBackoffPeriod(0, $request));
44362 }
44363
44364 public function testReturnsFalseWhenNullAndNoNext()
44365 {
44366 $request = new Request('GET', 'http://www.foo.com');
44367 $s = new TruncatedBackoffStrategy(2);
44368 $this->assertFalse($s->getBackoffPeriod(0, $request));
44369 }
44370
44371 public function testHasNext()
44372 {
44373 $a = new TruncatedBackoffStrategy(2);
44374 $b = new TruncatedBackoffStrategy(2);
44375 $a->setNext($b);
44376 $this->assertSame($b, $a->getNext());
44377 }
44378
44379 public function testSkipsOtherDecisionsInChainWhenOneReturnsTrue()
44380 {
44381 $a = new CallbackBackoffStrategy(function () { return null; }, true);
44382 $b = new CallbackBackoffStrategy(function () { return true; }, true);
44383 $c = new CallbackBackoffStrategy(function () { return null; }, true);
44384 $d = new CallbackBackoffStrategy(function () { return 10; }, false);
44385 $a->setNext($b);
44386 $b->setNext($c);
44387 $c->setNext($d);
44388 $this->assertEquals(10, $a->getBackoffPeriod(2, new Request('GET', 'http://www.foo.com')));
44389 }
44390
44391 public function testReturnsZeroWhenDecisionMakerReturnsTrueButNoFurtherStrategiesAreInTheChain()
44392 {
44393 $a = new CallbackBackoffStrategy(function () { return null; }, true);
44394 $b = new CallbackBackoffStrategy(function () { return true; }, true);
44395 $a->setNext($b);
44396 $this->assertSame(0, $a->getBackoffPeriod(2, new Request('GET', 'http://www.foo.com')));
44397 }
44398 }
44399 <?php
44400
44401 namespace Guzzle\Tests\Plugin\Backoff;
44402
44403 use Guzzle\Http\Message\Response;
44404 use Guzzle\Plugin\Backoff\TruncatedBackoffStrategy;
44405 use Guzzle\Plugin\Backoff\HttpBackoffStrategy;
44406 use Guzzle\Plugin\Backoff\ConstantBackoffStrategy;
44407
44408 /**
44409 * @covers Guzzle\Plugin\Backoff\TruncatedBackoffStrategy
44410 */
44411 class TruncatedBackoffStrategyTest extends \Guzzle\Tests\GuzzleTestCase
44412 {
44413 public function testRetriesWhenLessThanMax()
44414 {
44415 $strategy = new TruncatedBackoffStrategy(2);
44416 $this->assertTrue($strategy->makesDecision());
44417 $request = $this->getMock('Guzzle\Http\Message\Request', array(), array(), '', false);
44418 $this->assertFalse($strategy->getBackoffPeriod(0, $request));
44419 $this->assertFalse($strategy->getBackoffPeriod(1, $request));
44420 $this->assertFalse($strategy->getBackoffPeriod(2, $request));
44421
44422 $response = new Response(500);
44423 $strategy->setNext(new HttpBackoffStrategy(null, new ConstantBackoffStrategy(10)));
44424 $this->assertEquals(10, $strategy->getBackoffPeriod(0, $request, $response));
44425 $this->assertEquals(10, $strategy->getBackoffPeriod(1, $request, $response));
44426 $this->assertFalse($strategy->getBackoffPeriod(2, $request, $response));
44427 }
44428 }
44429 <?php
44430
44431 namespace Guzzle\Tests\Plugin\Oauth;
44432
44433 use Guzzle\Http\Message\RequestFactory;
44434 use Guzzle\Plugin\Oauth\OauthPlugin;
44435 use Guzzle\Common\Event;
44436
44437 /**
44438 * @covers Guzzle\Plugin\Oauth\OauthPlugin
44439 */
44440 class OauthPluginTest extends \Guzzle\Tests\GuzzleTestCase
44441 {
44442 const TIMESTAMP = '1327274290';
44443 const NONCE = 'e7aa11195ca58349bec8b5ebe351d3497eb9e603';
44444
44445 protected $config = array(
44446 'consumer_key' => 'foo',
44447 'consumer_secret' => 'bar',
44448 'token' => 'count',
44449 'token_secret' => 'dracula'
44450 );
44451
44452 protected function getRequest()
44453 {
44454 return RequestFactory::getInstance()->create('POST', 'http://www.test.com/path?a=b&c=d', null, array(
44455 'e' => 'f'
44456 ));
44457 }
44458
44459 public function testSubscribesToEvents()
44460 {
44461 $events = OauthPlugin::getSubscribedEvents();
44462 $this->assertArrayHasKey('request.before_send', $events);
44463 }
44464
44465 public function testAcceptsConfigurationData()
44466 {
44467 $p = new OauthPlugin($this->config);
44468
44469 // Access the config object
44470 $class = new \ReflectionClass($p);
44471 $property = $class->getProperty('config');
44472 $property->setAccessible(true);
44473 $config = $property->getValue($p);
44474
44475 $this->assertEquals('foo', $config['consumer_key']);
44476 $this->assertEquals('bar', $config['consumer_secret']);
44477 $this->assertEquals('count', $config['token']);
44478 $this->assertEquals('dracula', $config['token_secret']);
44479 $this->assertEquals('1.0', $config['version']);
44480 $this->assertEquals('HMAC-SHA1', $config['signature_method']);
44481 }
44482
44483 public function testCreatesStringToSignFromPostRequest()
44484 {
44485 $p = new OauthPlugin($this->config);
44486 $request = $this->getRequest();
44487 $signString = $p->getStringToSign($request, self::TIMESTAMP, self::NONCE);
44488
44489 $this->assertContains('&e=f', rawurldecode($signString));
44490
44491 $expectedSignString =
44492 // Method and URL
44493 'POST&http%3A%2F%2Fwww.test.com%2Fpath' .
44494 // Sorted parameters from query string and body
44495 '&a%3Db%26c%3Dd%26e%3Df%26oauth_consumer_key%3Dfoo' .
44496 '%26oauth_nonce%3De7aa11195ca58349bec8b5ebe351d3497eb9e603%26' .
44497 'oauth_signature_method%3DHMAC-SHA1' .
44498 '%26oauth_timestamp%3D' . self::TIMESTAMP . '%26oauth_token%3Dcount%26oauth_version%3D1.0';
44499
44500 $this->assertEquals($expectedSignString, $signString);
44501 }
44502
44503 public function testCreatesStringToSignIgnoringPostFields()
44504 {
44505 $config = $this->config;
44506 $config['disable_post_params'] = true;
44507 $p = new OauthPlugin($config);
44508 $request = $this->getRequest();
44509 $sts = rawurldecode($p->getStringToSign($request, self::TIMESTAMP, self::NONCE));
44510 $this->assertNotContains('&e=f', $sts);
44511 }
44512
44513 public function testCreatesStringToSignFromPostRequestWithCustomContentType()
44514 {
44515 $p = new OauthPlugin($this->config);
44516 $request = $this->getRequest();
44517 $request->setHeader('Content-Type', 'Foo');
44518 $this->assertEquals(
44519 // Method and URL
44520 'POST&http%3A%2F%2Fwww.test.com%2Fpath' .
44521 // Sorted parameters from query string and body
44522 '&a%3Db%26c%3Dd%26oauth_consumer_key%3Dfoo' .
44523 '%26oauth_nonce%3D'. self::NONCE .'%26' .
44524 'oauth_signature_method%3DHMAC-SHA1' .
44525 '%26oauth_timestamp%3D' . self::TIMESTAMP . '%26oauth_token%3Dcount%26oauth_version%3D1.0',
44526 $p->getStringToSign($request, self::TIMESTAMP, self::NONCE)
44527 );
44528 }
44529
44530 /**
44531 * @depends testCreatesStringToSignFromPostRequest
44532 */
44533 public function testConvertsBooleansToStrings()
44534 {
44535 $p = new OauthPlugin($this->config);
44536 $request = $this->getRequest();
44537 $request->getQuery()->set('a', true);
44538 $request->getQuery()->set('c', false);
44539 $this->assertContains('&a%3Dtrue%26c%3Dfalse', $p->getStringToSign($request, self::TIMESTAMP, self::NONCE));
44540 }
44541
44542 public function testCreatesStringToSignFromPostRequestWithNullValues()
44543 {
44544 $config = array(
44545 'consumer_key' => 'foo',
44546 'consumer_secret' => 'bar',
44547 'token' => null,
44548 'token_secret' => 'dracula'
44549 );
44550
44551 $p = new OauthPlugin($config);
44552 $request = $this->getRequest();
44553 $signString = $p->getStringToSign($request, self::TIMESTAMP, self::NONCE);
44554
44555 $this->assertContains('&e=f', rawurldecode($signString));
44556
44557 $expectedSignString = // Method and URL
44558 'POST&http%3A%2F%2Fwww.test.com%2Fpath' .
44559 // Sorted parameters from query string and body
44560 '&a%3Db%26c%3Dd%26e%3Df%26oauth_consumer_key%3Dfoo' .
44561 '%26oauth_nonce%3De7aa11195ca58349bec8b5ebe351d3497eb9e603%26' .
44562 'oauth_signature_method%3DHMAC-SHA1' .
44563 '%26oauth_timestamp%3D' . self::TIMESTAMP . '%26oauth_version%3D1.0';
44564
44565 $this->assertEquals($expectedSignString, $signString);
44566 }
44567
44568 /**
44569 * @depends testCreatesStringToSignFromPostRequest
44570 */
44571 public function testMultiDimensionalArray()
44572 {
44573 $p = new OauthPlugin($this->config);
44574 $request = $this->getRequest();
44575 $request->getQuery()->set('a', array('b' => array('e' => 'f', 'c' => 'd')));
44576 $this->assertContains('a%255Bb%255D%255Bc%255D%3Dd%26a%255Bb%255D%255Be%255D%3Df%26c%3Dd%26e%3Df%26', $p->getStringToSign($request, self::TIMESTAMP, self::NONCE));
44577 }
44578
44579 /**
44580 * @depends testCreatesStringToSignFromPostRequest
44581 */
44582 public function testSignsStrings()
44583 {
44584 $p = new OauthPlugin(array_merge($this->config, array(
44585 'signature_callback' => function($string, $key) {
44586 return "_{$string}|{$key}_";
44587 }
44588 )));
44589 $request = $this->getRequest();
44590 $sig = $p->getSignature($request, self::TIMESTAMP, self::NONCE);
44591 $this->assertEquals(
44592 '_POST&http%3A%2F%2Fwww.test.com%2Fpath&a%3Db%26c%3Dd%26e%3Df%26oauth_consumer_key%3Dfoo' .
44593 '%26oauth_nonce%3D'. self::NONCE .'%26oauth_signature_method%3DHMAC-SHA1' .
44594 '%26oauth_timestamp%3D' . self::TIMESTAMP . '%26oauth_token%3Dcount%26oauth_version%3D1.0|' .
44595 'bar&dracula_',
44596 base64_decode($sig)
44597 );
44598 }
44599
44600 /**
44601 * Test that the Oauth is signed correctly and that extra strings haven't been added
44602 * to the authorization header.
44603 */
44604 public function testSignsOauthRequests()
44605 {
44606 $p = new OauthPlugin($this->config);
44607 $event = new Event(array(
44608 'request' => $this->getRequest(),
44609 'timestamp' => self::TIMESTAMP
44610 ));
44611 $params = $p->onRequestBeforeSend($event);
44612
44613 $this->assertTrue($event['request']->hasHeader('Authorization'));
44614
44615 $authorizationHeader = (string)$event['request']->getHeader('Authorization');
44616
44617 $this->assertStringStartsWith('OAuth ', $authorizationHeader);
44618
44619 $stringsToCheck = array(
44620 'oauth_consumer_key="foo"',
44621 'oauth_nonce="'.urlencode($params['oauth_nonce']).'"',
44622 'oauth_signature="'.urlencode($params['oauth_signature']).'"',
44623 'oauth_signature_method="HMAC-SHA1"',
44624 'oauth_timestamp="' . self::TIMESTAMP . '"',
44625 'oauth_token="count"',
44626 'oauth_version="1.0"',
44627 );
44628
44629 $totalLength = strlen('OAuth ');
44630
44631 //Separator is not used before first parameter.
44632 $separator = '';
44633
44634 foreach ($stringsToCheck as $stringToCheck) {
44635 $this->assertContains($stringToCheck, $authorizationHeader);
44636 $totalLength += strlen($separator);
44637 $totalLength += strlen($stringToCheck);
44638 $separator = ', ';
44639 }
44640
44641 // Technically this test is not universally valid. It would be allowable to have extra \n characters
44642 // in the Authorization header. However Guzzle does not do this, so we just perform a simple check
44643 // on length to validate the Authorization header is composed of only the strings above.
44644 $this->assertEquals($totalLength, strlen($authorizationHeader), 'Authorization has extra characters i.e. contains extra elements compared to stringsToCheck.');
44645 }
44646
44647 public function testDoesNotAddFalseyValuesToAuthorization()
44648 {
44649 unset($this->config['token']);
44650 $p = new OauthPlugin($this->config);
44651 $event = new Event(array('request' => $this->getRequest(), 'timestamp' => self::TIMESTAMP));
44652 $p->onRequestBeforeSend($event);
44653 $this->assertTrue($event['request']->hasHeader('Authorization'));
44654 $this->assertNotContains('oauth_token=', (string) $event['request']->getHeader('Authorization'));
44655 }
44656
44657 public function testOptionalOauthParametersAreNotAutomaticallyAdded()
44658 {
44659 // The only required Oauth parameters are the consumer key and secret. That is enough credentials
44660 // for signing oauth requests.
44661 $config = array(
44662 'consumer_key' => 'foo',
44663 'consumer_secret' => 'bar',
44664 );
44665
44666 $plugin = new OauthPlugin($config);
44667 $event = new Event(array(
44668 'request' => $this->getRequest(),
44669 'timestamp' => self::TIMESTAMP
44670 ));
44671
44672 $timestamp = $plugin->getTimestamp($event);
44673 $request = $event['request'];
44674 $nonce = $plugin->generateNonce($request);
44675
44676 $paramsToSign = $plugin->getParamsToSign($request, $timestamp, $nonce);
44677
44678 $optionalParams = array(
44679 'callback' => 'oauth_callback',
44680 'token' => 'oauth_token',
44681 'verifier' => 'oauth_verifier',
44682 'token_secret' => 'token_secret'
44683 );
44684
44685 foreach ($optionalParams as $optionName => $oauthName) {
44686 $this->assertArrayNotHasKey($oauthName, $paramsToSign, "Optional Oauth param '$oauthName' was not set via config variable '$optionName', but it is listed in getParamsToSign().");
44687 }
44688 }
44689 }
44690 <?php
44691
44692 namespace Guzzle\Tests\Plugin\Cookie\CookieJar;
44693
44694 use Guzzle\Plugin\Cookie\Cookie;
44695 use Guzzle\Plugin\Cookie\CookieJar\FileCookieJar;
44696
44697 /**
44698 * @covers Guzzle\Plugin\Cookie\CookieJar\FileCookieJar
44699 */
44700 class FileCookieJarTest extends \Guzzle\Tests\GuzzleTestCase
44701 {
44702 private $file;
44703
44704 public function setUp()
44705 {
44706 $this->file = tempnam('/tmp', 'file-cookies');
44707 }
44708
44709 public function testLoadsFromFileFile()
44710 {
44711 $jar = new FileCookieJar($this->file);
44712 $this->assertEquals(array(), $jar->all());
44713 unlink($this->file);
44714 }
44715
44716 public function testPersistsToFileFile()
44717 {
44718 $jar = new FileCookieJar($this->file);
44719 $jar->add(new Cookie(array(
44720 'name' => 'foo',
44721 'value' => 'bar',
44722 'domain' => 'foo.com',
44723 'expires' => time() + 1000
44724 )));
44725 $jar->add(new Cookie(array(
44726 'name' => 'baz',
44727 'value' => 'bar',
44728 'domain' => 'foo.com',
44729 'expires' => time() + 1000
44730 )));
44731 $jar->add(new Cookie(array(
44732 'name' => 'boo',
44733 'value' => 'bar',
44734 'domain' => 'foo.com',
44735 )));
44736
44737 $this->assertEquals(3, count($jar));
44738 unset($jar);
44739
44740 // Make sure it wrote to the file
44741 $contents = file_get_contents($this->file);
44742 $this->assertNotEmpty($contents);
44743
44744 // Load the cookieJar from the file
44745 $jar = new FileCookieJar($this->file);
44746
44747 // Weeds out temporary and session cookies
44748 $this->assertEquals(2, count($jar));
44749 unset($jar);
44750 unlink($this->file);
44751 }
44752 }
44753 <?php
44754
44755 namespace Guzzle\Tests\Plugin\Cookie\CookieJar;
44756
44757 use Guzzle\Plugin\Cookie\Cookie;
44758 use Guzzle\Plugin\Cookie\CookieJar\ArrayCookieJar;
44759 use Guzzle\Http\Message\Response;
44760 use Guzzle\Http\Message\Request;
44761
44762 /**
44763 * @covers Guzzle\Plugin\Cookie\CookieJar\ArrayCookieJar
44764 */
44765 class ArrayCookieJarTest extends \Guzzle\Tests\GuzzleTestCase
44766 {
44767 /**
44768 * @var ArrayCookieJar
44769 */
44770 private $jar;
44771
44772 public function setUp()
44773 {
44774 $this->jar = new ArrayCookieJar();
44775 }
44776
44777 protected function getTestCookies()
44778 {
44779 return array(
44780 new Cookie(array('name' => 'foo', 'value' => 'bar', 'domain' => 'foo.com', 'path' => '/', 'discard' => true)),
44781 new Cookie(array('name' => 'test', 'value' => '123', 'domain' => 'baz.com', 'path' => '/foo', 'expires' => 2)),
44782 new Cookie(array('name' => 'you', 'value' => '123', 'domain' => 'bar.com', 'path' => '/boo', 'expires' => time() + 1000))
44783 );
44784 }
44785
44786 /**
44787 * Provides test data for cookie cookieJar retrieval
44788 */
44789 public function getCookiesDataProvider()
44790 {
44791 return array(
44792 array(array('foo', 'baz', 'test', 'muppet', 'googoo'), '', '', '', false),
44793 array(array('foo', 'baz', 'muppet', 'googoo'), '', '', '', true),
44794 array(array('googoo'), 'www.example.com', '', '', false),
44795 array(array('muppet', 'googoo'), 'test.y.example.com', '', '', false),
44796 array(array('foo', 'baz'), 'example.com', '', '', false),
44797 array(array('muppet'), 'x.y.example.com', '/acme/', '', false),
44798 array(array('muppet'), 'x.y.example.com', '/acme/test/', '', false),
44799 array(array('googoo'), 'x.y.example.com', '/test/acme/test/', '', false),
44800 array(array('foo', 'baz'), 'example.com', '', '', false),
44801 array(array('baz'), 'example.com', '', 'baz', false),
44802 );
44803 }
44804
44805 public function testStoresAndRetrievesCookies()
44806 {
44807 $cookies = $this->getTestCookies();
44808 foreach ($cookies as $cookie) {
44809 $this->assertTrue($this->jar->add($cookie));
44810 }
44811
44812 $this->assertEquals(3, count($this->jar));
44813 $this->assertEquals(3, count($this->jar->getIterator()));
44814 $this->assertEquals($cookies, $this->jar->all(null, null, null, false, false));
44815 }
44816
44817 public function testRemovesExpiredCookies()
44818 {
44819 $cookies = $this->getTestCookies();
44820 foreach ($this->getTestCookies() as $cookie) {
44821 $this->jar->add($cookie);
44822 }
44823 $this->jar->removeExpired();
44824 $this->assertEquals(array($cookies[0], $cookies[2]), $this->jar->all());
44825 }
44826
44827 public function testRemovesTemporaryCookies()
44828 {
44829 $cookies = $this->getTestCookies();
44830 foreach ($this->getTestCookies() as $cookie) {
44831 $this->jar->add($cookie);
44832 }
44833 $this->jar->removeTemporary();
44834 $this->assertEquals(array($cookies[2]), $this->jar->all());
44835 }
44836
44837 public function testIsSerializable()
44838 {
44839 $this->assertEquals('[]', $this->jar->serialize());
44840 $this->jar->unserialize('[]');
44841 $this->assertEquals(array(), $this->jar->all());
44842
44843 $cookies = $this->getTestCookies();
44844 foreach ($this->getTestCookies() as $cookie) {
44845 $this->jar->add($cookie);
44846 }
44847
44848 // Remove discard and expired cookies
44849 $serialized = $this->jar->serialize();
44850 $data = json_decode($serialized, true);
44851 $this->assertEquals(1, count($data));
44852
44853 $a = new ArrayCookieJar();
44854 $a->unserialize($serialized);
44855 $this->assertEquals(1, count($a));
44856 }
44857
44858 public function testRemovesSelectively()
44859 {
44860 $cookies = $this->getTestCookies();
44861 foreach ($this->getTestCookies() as $cookie) {
44862 $this->jar->add($cookie);
44863 }
44864
44865 // Remove foo.com cookies
44866 $this->jar->remove('foo.com');
44867 $this->assertEquals(2, count($this->jar));
44868 // Try again, removing no further cookies
44869 $this->jar->remove('foo.com');
44870 $this->assertEquals(2, count($this->jar));
44871
44872 // Remove bar.com cookies with path of /boo
44873 $this->jar->remove('bar.com', '/boo');
44874 $this->assertEquals(1, count($this->jar));
44875
44876 // Remove cookie by name
44877 $this->jar->remove(null, null, 'test');
44878 $this->assertEquals(0, count($this->jar));
44879 }
44880
44881 public function testDoesNotAddIncompleteCookies()
44882 {
44883 $this->assertEquals(false, $this->jar->add(new Cookie()));
44884 $this->assertFalse($this->jar->add(new Cookie(array(
44885 'name' => 'foo'
44886 ))));
44887 $this->assertFalse($this->jar->add(new Cookie(array(
44888 'name' => false
44889 ))));
44890 $this->assertFalse($this->jar->add(new Cookie(array(
44891 'name' => true
44892 ))));
44893 $this->assertFalse($this->jar->add(new Cookie(array(
44894 'name' => 'foo',
44895 'domain' => 'foo.com'
44896 ))));
44897 }
44898
44899 public function testDoesAddValidCookies()
44900 {
44901 $this->assertTrue($this->jar->add(new Cookie(array(
44902 'name' => 'foo',
44903 'domain' => 'foo.com',
44904 'value' => 0
44905 ))));
44906 $this->assertTrue($this->jar->add(new Cookie(array(
44907 'name' => 'foo',
44908 'domain' => 'foo.com',
44909 'value' => 0.0
44910 ))));
44911 $this->assertTrue($this->jar->add(new Cookie(array(
44912 'name' => 'foo',
44913 'domain' => 'foo.com',
44914 'value' => '0'
44915 ))));
44916 }
44917
44918 public function testOverwritesCookiesThatAreOlderOrDiscardable()
44919 {
44920 $t = time() + 1000;
44921 $data = array(
44922 'name' => 'foo',
44923 'value' => 'bar',
44924 'domain' => '.example.com',
44925 'path' => '/',
44926 'max_age' => '86400',
44927 'port' => array(80, 8080),
44928 'version' => '1',
44929 'secure' => true,
44930 'discard' => true,
44931 'expires' => $t
44932 );
44933
44934 // Make sure that the discard cookie is overridden with the non-discard
44935 $this->assertTrue($this->jar->add(new Cookie($data)));
44936
44937 unset($data['discard']);
44938 $this->assertTrue($this->jar->add(new Cookie($data)));
44939 $this->assertEquals(1, count($this->jar));
44940
44941 $c = $this->jar->all();
44942 $this->assertEquals(false, $c[0]->getDiscard());
44943
44944 // Make sure it doesn't duplicate the cookie
44945 $this->jar->add(new Cookie($data));
44946 $this->assertEquals(1, count($this->jar));
44947
44948 // Make sure the more future-ful expiration date supersede the other
44949 $data['expires'] = time() + 2000;
44950 $this->assertTrue($this->jar->add(new Cookie($data)));
44951 $this->assertEquals(1, count($this->jar));
44952 $c = $this->jar->all();
44953 $this->assertNotEquals($t, $c[0]->getExpires());
44954 }
44955
44956 public function testOverwritesCookiesThatHaveChanged()
44957 {
44958 $t = time() + 1000;
44959 $data = array(
44960 'name' => 'foo',
44961 'value' => 'bar',
44962 'domain' => '.example.com',
44963 'path' => '/',
44964 'max_age' => '86400',
44965 'port' => array(80, 8080),
44966 'version' => '1',
44967 'secure' => true,
44968 'discard' => true,
44969 'expires' => $t
44970 );
44971
44972 // Make sure that the discard cookie is overridden with the non-discard
44973 $this->assertTrue($this->jar->add(new Cookie($data)));
44974
44975 $data['value'] = 'boo';
44976 $this->assertTrue($this->jar->add(new Cookie($data)));
44977 $this->assertEquals(1, count($this->jar));
44978
44979 // Changing the value plus a parameter also must overwrite the existing one
44980 $data['value'] = 'zoo';
44981 $data['secure'] = false;
44982 $this->assertTrue($this->jar->add(new Cookie($data)));
44983 $this->assertEquals(1, count($this->jar));
44984
44985 $c = $this->jar->all();
44986 $this->assertEquals('zoo', $c[0]->getValue());
44987 }
44988
44989 public function testAddsCookiesFromResponseWithNoRequest()
44990 {
44991 $response = new Response(200, array(
44992 'Set-Cookie' => array(
44993 "fpc=d=.Hm.yh4.1XmJWjJfs4orLQzKzPImxklQoxXSHOZATHUSEFciRueW_7704iYUtsXNEXq0M92Px2glMdWypmJ7HIQl6XIUvrZimWjQ3vIdeuRbI.FNQMAfcxu_XN1zSx7l.AcPdKL6guHc2V7hIQFhnjRW0rxm2oHY1P4bGQxFNz7f.tHm12ZD3DbdMDiDy7TBXsuP4DM-&v=2; expires=Fri, 02-Mar-2019 02:17:40 GMT; path=/; domain=127.0.0.1",
44994 "FPCK3=AgBNbvoQAGpGEABZLRAAbFsQAF1tEABkDhAAeO0=; expires=Sat, 02-Apr-2019 02:17:40 GMT; path=/; domain=127.0.0.1",
44995 "CH=deleted; expires=Wed, 03-Mar-2010 02:17:39 GMT; path=/; domain=127.0.0.1",
44996 "CH=AgBNbvoQAAEcEAApuhAAMJcQADQvEAAvGxAALe0QAD6uEAATwhAAC1AQAC8t; expires=Sat, 02-Apr-2019 02:17:40 GMT; path=/; domain=127.0.0.1"
44997 )
44998 ));
44999
45000 $this->jar->addCookiesFromResponse($response);
45001 $this->assertEquals(3, count($this->jar));
45002 $this->assertEquals(1, count($this->jar->all(null, null, 'fpc')));
45003 $this->assertEquals(1, count($this->jar->all(null, null, 'FPCK3')));
45004 $this->assertEquals(1, count($this->jar->all(null, null, 'CH')));
45005 }
45006
45007 public function testAddsCookiesFromResponseWithRequest()
45008 {
45009 $response = new Response(200, array(
45010 'Set-Cookie' => "fpc=d=.Hm.yh4.1XmJWjJfs4orLQzKzPImxklQoxXSHOZATHUSEFciRueW_7704iYUtsXNEXq0M92Px2glMdWypmJ7HIQl6XIUvrZimWjQ3vIdeuRbI.FNQMAfcxu_XN1zSx7l.AcPdKL6guHc2V7hIQFhnjRW0rxm2oHY1P4bGQxFNz7f.tHm12ZD3DbdMDiDy7TBXsuP4DM-&v=2; expires=Fri, 02-Mar-2019 02:17:40 GMT;"
45011 ));
45012 $request = new Request('GET', 'http://www.example.com');
45013 $this->jar->addCookiesFromResponse($response, $request);
45014 $this->assertEquals(1, count($this->jar));
45015 }
45016
45017 public function getMatchingCookiesDataProvider()
45018 {
45019 return array(
45020 array('https://example.com', array(0)),
45021 array('http://example.com', array()),
45022 array('https://example.com:8912', array()),
45023 array('https://foo.example.com', array(0)),
45024 array('http://foo.example.com/test/acme/', array(4))
45025 );
45026 }
45027
45028 /**
45029 * @dataProvider getMatchingCookiesDataProvider
45030 */
45031 public function testReturnsCookiesMatchingRequests($url, $cookies)
45032 {
45033 $bag = array(
45034 new Cookie(array(
45035 'name' => 'foo',
45036 'value' => 'bar',
45037 'domain' => 'example.com',
45038 'path' => '/',
45039 'max_age' => '86400',
45040 'port' => array(443, 8080),
45041 'version' => '1',
45042 'secure' => true
45043 )),
45044 new Cookie(array(
45045 'name' => 'baz',
45046 'value' => 'foobar',
45047 'domain' => 'example.com',
45048 'path' => '/',
45049 'max_age' => '86400',
45050 'port' => array(80, 8080),
45051 'version' => '1',
45052 'secure' => true
45053 )),
45054 new Cookie(array(
45055 'name' => 'test',
45056 'value' => '123',
45057 'domain' => 'www.foobar.com',
45058 'path' => '/path/',
45059 'discard' => true
45060 )),
45061 new Cookie(array(
45062 'name' => 'muppet',
45063 'value' => 'cookie_monster',
45064 'domain' => '.y.example.com',
45065 'path' => '/acme/',
45066 'comment' => 'Comment goes here...',
45067 'expires' => time() + 86400
45068 )),
45069 new Cookie(array(
45070 'name' => 'googoo',
45071 'value' => 'gaga',
45072 'domain' => '.example.com',
45073 'path' => '/test/acme/',
45074 'max_age' => 1500,
45075 'version' => 2
45076 ))
45077 );
45078
45079 foreach ($bag as $cookie) {
45080 $this->jar->add($cookie);
45081 }
45082
45083 $request = new Request('GET', $url);
45084 $results = $this->jar->getMatchingCookies($request);
45085 $this->assertEquals(count($cookies), count($results));
45086 foreach ($cookies as $i) {
45087 $this->assertContains($bag[$i], $results);
45088 }
45089 }
45090
45091 /**
45092 * @expectedException \Guzzle\Plugin\Cookie\Exception\InvalidCookieException
45093 * @expectedExceptionMessage The cookie name must not contain invalid characters: abc:@123
45094 */
45095 public function testThrowsExceptionWithStrictMode()
45096 {
45097 $a = new ArrayCookieJar();
45098 $a->setStrictMode(true);
45099 $a->add(new Cookie(array(
45100 'name' => 'abc:@123',
45101 'value' => 'foo',
45102 'domain' => 'bar'
45103 )));
45104 }
45105 }
45106 <?php
45107
45108 namespace Guzzle\Tests\Plugin\Cookie;
45109
45110 use Guzzle\Common\Event;
45111 use Guzzle\Plugin\Cookie\Cookie;
45112 use Guzzle\Plugin\Cookie\CookieJar\ArrayCookieJar;
45113 use Guzzle\Http\Client;
45114 use Guzzle\Http\Message\Response;
45115 use Guzzle\Plugin\Cookie\CookiePlugin;
45116
45117 /**
45118 * @group server
45119 * @covers Guzzle\Plugin\Cookie\CookiePlugin
45120 */
45121 class CookiePluginTest extends \Guzzle\Tests\GuzzleTestCase
45122 {
45123 public function testExtractsAndStoresCookies()
45124 {
45125 $response = new Response(200);
45126 $mock = $this->getMockBuilder('Guzzle\Plugin\Cookie\CookieJar\ArrayCookieJar')
45127 ->setMethods(array('addCookiesFromResponse'))
45128 ->getMock();
45129
45130 $mock->expects($this->exactly(1))
45131 ->method('addCookiesFromResponse')
45132 ->with($response);
45133
45134 $plugin = new CookiePlugin($mock);
45135 $plugin->onRequestSent(new Event(array(
45136 'response' => $response
45137 )));
45138 }
45139
45140 public function testAddsCookiesToRequests()
45141 {
45142 $cookie = new Cookie(array(
45143 'name' => 'foo',
45144 'value' => 'bar'
45145 ));
45146
45147 $mock = $this->getMockBuilder('Guzzle\Plugin\Cookie\CookieJar\ArrayCookieJar')
45148 ->setMethods(array('getMatchingCookies'))
45149 ->getMock();
45150
45151 $mock->expects($this->once())
45152 ->method('getMatchingCookies')
45153 ->will($this->returnValue(array($cookie)));
45154
45155 $plugin = new CookiePlugin($mock);
45156
45157 $client = new Client();
45158 $client->getEventDispatcher()->addSubscriber($plugin);
45159
45160 $request = $client->get('http://www.example.com');
45161 $plugin->onRequestBeforeSend(new Event(array(
45162 'request' => $request
45163 )));
45164
45165 $this->assertEquals('bar', $request->getCookie('foo'));
45166 }
45167
45168 public function testCookiesAreExtractedFromRedirectResponses()
45169 {
45170 $plugin = new CookiePlugin(new ArrayCookieJar());
45171 $this->getServer()->flush();
45172 $this->getServer()->enqueue(array(
45173 "HTTP/1.1 302 Moved Temporarily\r\n" .
45174 "Set-Cookie: test=583551; expires=Wednesday, 23-Mar-2050 19:49:45 GMT; path=/\r\n" .
45175 "Location: /redirect\r\n\r\n",
45176 "HTTP/1.1 200 OK\r\n" .
45177 "Content-Length: 0\r\n\r\n",
45178 "HTTP/1.1 200 OK\r\n" .
45179 "Content-Length: 0\r\n\r\n"
45180 ));
45181
45182 $client = new Client($this->getServer()->getUrl());
45183 $client->getEventDispatcher()->addSubscriber($plugin);
45184
45185 $client->get()->send();
45186 $request = $client->get();
45187 $request->send();
45188 $this->assertEquals('test=583551', $request->getHeader('Cookie'));
45189
45190 $requests = $this->getServer()->getReceivedRequests(true);
45191 // Confirm subsequent requests have the cookie.
45192 $this->assertEquals('test=583551', $requests[2]->getHeader('Cookie'));
45193 // Confirm the redirected request has the cookie.
45194 $this->assertEquals('test=583551', $requests[1]->getHeader('Cookie'));
45195 }
45196
45197 public function testCookiesAreNotAddedWhenParamIsSet()
45198 {
45199 $jar = new ArrayCookieJar();
45200 $plugin = new CookiePlugin($jar);
45201
45202 $jar->add(new Cookie(array(
45203 'domain' => 'example.com',
45204 'path' => '/',
45205 'name' => 'test',
45206 'value' => 'hi',
45207 'expires' => time() + 3600
45208 )));
45209
45210 $client = new Client('http://example.com');
45211 $client->getEventDispatcher()->addSubscriber($plugin);
45212
45213 // Ensure that it is normally added
45214 $request = $client->get();
45215 $request->setResponse(new Response(200), true);
45216 $request->send();
45217 $this->assertEquals('hi', $request->getCookie('test'));
45218
45219 // Now ensure that it is not added
45220 $request = $client->get();
45221 $request->getParams()->set('cookies.disable', true);
45222 $request->setResponse(new Response(200), true);
45223 $request->send();
45224 $this->assertNull($request->getCookie('test'));
45225 }
45226
45227 public function testProvidesCookieJar()
45228 {
45229 $jar = new ArrayCookieJar();
45230 $plugin = new CookiePlugin($jar);
45231 $this->assertSame($jar, $plugin->getCookieJar());
45232 }
45233 }
45234 <?php
45235
45236 namespace Guzzle\Tests\Plugin\Cookie;
45237
45238 use Guzzle\Plugin\Cookie\Cookie;
45239
45240 /**
45241 * @covers Guzzle\Plugin\Cookie\Cookie
45242 */
45243 class CookieTest extends \Guzzle\Tests\GuzzleTestCase
45244 {
45245 public function testInitializesDefaultValues()
45246 {
45247 $cookie = new Cookie();
45248 $this->assertEquals('/', $cookie->getPath());
45249 $this->assertEquals(array(), $cookie->getPorts());
45250 }
45251
45252 public function testConvertsDateTimeMaxAgeToUnixTimestamp()
45253 {
45254 $cookie = new Cookie(array(
45255 'expires' => 'November 20, 1984'
45256 ));
45257 $this->assertTrue(is_numeric($cookie->getExpires()));
45258 }
45259
45260 public function testAddsExpiresBasedOnMaxAge()
45261 {
45262 $t = time();
45263 $cookie = new Cookie(array(
45264 'max_age' => 100
45265 ));
45266 $this->assertEquals($t + 100, $cookie->getExpires());
45267 }
45268
45269 public function testHoldsValues()
45270 {
45271 $t = time();
45272 $data = array(
45273 'name' => 'foo',
45274 'value' => 'baz',
45275 'path' => '/bar',
45276 'domain' => 'baz.com',
45277 'expires' => $t,
45278 'max_age' => 100,
45279 'comment' => 'Hi',
45280 'comment_url' => 'foo.com',
45281 'port' => array(1, 2),
45282 'version' => 2,
45283 'secure' => true,
45284 'discard' => true,
45285 'http_only' => true,
45286 'data' => array(
45287 'foo' => 'baz',
45288 'bar' => 'bam'
45289 )
45290 );
45291
45292 $cookie = new Cookie($data);
45293 $this->assertEquals($data, $cookie->toArray());
45294
45295 $this->assertEquals('foo', $cookie->getName());
45296 $this->assertEquals('baz', $cookie->getValue());
45297 $this->assertEquals('baz.com', $cookie->getDomain());
45298 $this->assertEquals('/bar', $cookie->getPath());
45299 $this->assertEquals($t, $cookie->getExpires());
45300 $this->assertEquals(100, $cookie->getMaxAge());
45301 $this->assertEquals('Hi', $cookie->getComment());
45302 $this->assertEquals('foo.com', $cookie->getCommentUrl());
45303 $this->assertEquals(array(1, 2), $cookie->getPorts());
45304 $this->assertEquals(2, $cookie->getVersion());
45305 $this->assertTrue($cookie->getSecure());
45306 $this->assertTrue($cookie->getDiscard());
45307 $this->assertTrue($cookie->getHttpOnly());
45308 $this->assertEquals('baz', $cookie->getAttribute('foo'));
45309 $this->assertEquals('bam', $cookie->getAttribute('bar'));
45310 $this->assertEquals(array(
45311 'foo' => 'baz',
45312 'bar' => 'bam'
45313 ), $cookie->getAttributes());
45314
45315 $cookie->setName('a')
45316 ->setValue('b')
45317 ->setPath('c')
45318 ->setDomain('bar.com')
45319 ->setExpires(10)
45320 ->setMaxAge(200)
45321 ->setComment('e')
45322 ->setCommentUrl('f')
45323 ->setPorts(array(80))
45324 ->setVersion(3)
45325 ->setSecure(false)
45326 ->setHttpOnly(false)
45327 ->setDiscard(false)
45328 ->setAttribute('snoop', 'dog');
45329
45330 $this->assertEquals('a', $cookie->getName());
45331 $this->assertEquals('b', $cookie->getValue());
45332 $this->assertEquals('c', $cookie->getPath());
45333 $this->assertEquals('bar.com', $cookie->getDomain());
45334 $this->assertEquals(10, $cookie->getExpires());
45335 $this->assertEquals(200, $cookie->getMaxAge());
45336 $this->assertEquals('e', $cookie->getComment());
45337 $this->assertEquals('f', $cookie->getCommentUrl());
45338 $this->assertEquals(array(80), $cookie->getPorts());
45339 $this->assertEquals(3, $cookie->getVersion());
45340 $this->assertFalse($cookie->getSecure());
45341 $this->assertFalse($cookie->getDiscard());
45342 $this->assertFalse($cookie->getHttpOnly());
45343 $this->assertEquals('dog', $cookie->getAttribute('snoop'));
45344 }
45345
45346 public function testDeterminesIfExpired()
45347 {
45348 $c = new Cookie();
45349 $c->setExpires(10);
45350 $this->assertTrue($c->isExpired());
45351 $c->setExpires(time() + 10000);
45352 $this->assertFalse($c->isExpired());
45353 }
45354
45355 public function testMatchesPorts()
45356 {
45357 $cookie = new Cookie();
45358 // Always matches when nothing is set
45359 $this->assertTrue($cookie->matchesPort(2));
45360
45361 $cookie->setPorts(array(1, 2));
45362 $this->assertTrue($cookie->matchesPort(2));
45363 $this->assertFalse($cookie->matchesPort(100));
45364 }
45365
45366 public function testMatchesDomain()
45367 {
45368 $cookie = new Cookie();
45369 $this->assertTrue($cookie->matchesDomain('baz.com'));
45370
45371 $cookie->setDomain('baz.com');
45372 $this->assertTrue($cookie->matchesDomain('baz.com'));
45373 $this->assertFalse($cookie->matchesDomain('bar.com'));
45374
45375 $cookie->setDomain('.baz.com');
45376 $this->assertTrue($cookie->matchesDomain('.baz.com'));
45377 $this->assertTrue($cookie->matchesDomain('foo.baz.com'));
45378 $this->assertFalse($cookie->matchesDomain('baz.bar.com'));
45379 $this->assertTrue($cookie->matchesDomain('baz.com'));
45380
45381 $cookie->setDomain('.127.0.0.1');
45382 $this->assertTrue($cookie->matchesDomain('127.0.0.1'));
45383
45384 $cookie->setDomain('127.0.0.1');
45385 $this->assertTrue($cookie->matchesDomain('127.0.0.1'));
45386
45387 $cookie->setDomain('.com.');
45388 $this->assertFalse($cookie->matchesDomain('baz.com'));
45389
45390 $cookie->setDomain('.local');
45391 $this->assertTrue($cookie->matchesDomain('example.local'));
45392 }
45393
45394 public function testMatchesPath()
45395 {
45396 $cookie = new Cookie();
45397 $this->assertTrue($cookie->matchesPath('/foo'));
45398
45399 $cookie->setPath('/foo');
45400 $this->assertTrue($cookie->matchesPath('/foo'));
45401 $this->assertTrue($cookie->matchesPath('/foo/bar'));
45402 $this->assertFalse($cookie->matchesPath('/bar'));
45403 }
45404
45405 public function cookieValidateProvider()
45406 {
45407 return array(
45408 array('foo', 'baz', 'bar', true),
45409 array('0', '0', '0', true),
45410 array('', 'baz', 'bar', 'The cookie name must not be empty'),
45411 array('foo', '', 'bar', 'The cookie value must not be empty'),
45412 array('foo', 'baz', '', 'The cookie domain must not be empty'),
45413 array('foo\\', 'baz', '0', 'The cookie name must not contain invalid characters: foo\\'),
45414 );
45415 }
45416
45417 /**
45418 * @dataProvider cookieValidateProvider
45419 */
45420 public function testValidatesCookies($name, $value, $domain, $result)
45421 {
45422 $cookie = new Cookie(array(
45423 'name' => $name,
45424 'value' => $value,
45425 'domain' => $domain
45426 ));
45427 $this->assertSame($result, $cookie->validate());
45428 }
45429
45430 public function testCreatesInvalidCharacterString()
45431 {
45432 $m = new \ReflectionMethod('Guzzle\Plugin\Cookie\Cookie', 'getInvalidCharacters');
45433 $m->setAccessible(true);
45434 $p = new \ReflectionProperty('Guzzle\Plugin\Cookie\Cookie', 'invalidCharString');
45435 $p->setAccessible(true);
45436 $p->setValue('');
45437 // Expects a string containing 51 invalid characters
45438 $this->assertEquals(51, strlen($m->invoke($m)));
45439 $this->assertContains('@', $m->invoke($m));
45440 }
45441 }
45442 <?php
45443
45444 namespace Guzzle\Tests\Plugin\Mock;
45445
45446 use Guzzle\Common\Event;
45447 use Guzzle\Http\EntityBody;
45448 use Guzzle\Http\Message\Response;
45449 use Guzzle\Plugin\Mock\MockPlugin;
45450 use Guzzle\Http\Client;
45451 use Guzzle\Http\Exception\CurlException;
45452
45453 /**
45454 * @covers Guzzle\Plugin\Mock\MockPlugin
45455 */
45456 class MockPluginTest extends \Guzzle\Tests\GuzzleTestCase
45457 {
45458 public function testDescribesSubscribedEvents()
45459 {
45460 $this->assertInternalType('array', MockPlugin::getSubscribedEvents());
45461 }
45462
45463 public function testDescribesEvents()
45464 {
45465 $this->assertInternalType('array', MockPlugin::getAllEvents());
45466 }
45467
45468 public function testCanBeTemporary()
45469 {
45470 $plugin = new MockPlugin();
45471 $this->assertFalse($plugin->isTemporary());
45472 $plugin = new MockPlugin(null, true);
45473 $this->assertTrue($plugin->isTemporary());
45474 }
45475
45476 public function testIsCountable()
45477 {
45478 $plugin = new MockPlugin();
45479 $plugin->addResponse(Response::fromMessage("HTTP/1.1 200 OK\r\nContent-Length: 0\r\n\r\n"));
45480 $this->assertEquals(1, count($plugin));
45481 }
45482
45483 /**
45484 * @depends testIsCountable
45485 */
45486 public function testCanClearQueue()
45487 {
45488 $plugin = new MockPlugin();
45489 $plugin->addResponse(Response::fromMessage("HTTP/1.1 200 OK\r\nContent-Length: 0\r\n\r\n"));
45490 $plugin->clearQueue();
45491 $this->assertEquals(0, count($plugin));
45492 }
45493
45494 public function testCanInspectQueue()
45495 {
45496 $plugin = new MockPlugin();
45497 $this->assertInternalType('array', $plugin->getQueue());
45498 $plugin->addResponse(Response::fromMessage("HTTP/1.1 200 OK\r\nContent-Length: 0\r\n\r\n"));
45499 $queue = $plugin->getQueue();
45500 $this->assertInternalType('array', $queue);
45501 $this->assertEquals(1, count($queue));
45502 }
45503
45504 public function testRetrievesResponsesFromFiles()
45505 {
45506 $response = MockPlugin::getMockFile(__DIR__ . '/../../TestData/mock_response');
45507 $this->assertInstanceOf('Guzzle\\Http\\Message\\Response', $response);
45508 $this->assertEquals(200, $response->getStatusCode());
45509 }
45510
45511 /**
45512 * @expectedException InvalidArgumentException
45513 */
45514 public function testThrowsExceptionWhenResponseFileIsNotFound()
45515 {
45516 MockPlugin::getMockFile('missing/filename');
45517 }
45518
45519 /**
45520 * @expectedException InvalidArgumentException
45521 */
45522 public function testInvalidResponsesThrowAnException()
45523 {
45524 $p = new MockPlugin();
45525 $p->addResponse($this);
45526 }
45527
45528 public function testAddsResponseObjectsToQueue()
45529 {
45530 $p = new MockPlugin();
45531 $response = Response::fromMessage("HTTP/1.1 200 OK\r\nContent-Length: 0\r\n\r\n");
45532 $p->addResponse($response);
45533 $this->assertEquals(array($response), $p->getQueue());
45534 }
45535
45536 public function testAddsResponseFilesToQueue()
45537 {
45538 $p = new MockPlugin();
45539 $p->addResponse(__DIR__ . '/../../TestData/mock_response');
45540 $this->assertEquals(1, count($p));
45541 }
45542
45543 /**
45544 * @depends testAddsResponseFilesToQueue
45545 */
45546 public function testAddsMockResponseToRequestFromClient()
45547 {
45548 $p = new MockPlugin();
45549 $response = MockPlugin::getMockFile(__DIR__ . '/../../TestData/mock_response');
45550 $p->addResponse($response);
45551
45552 $client = new Client('http://localhost:123/');
45553 $client->getEventDispatcher()->addSubscriber($p, 9999);
45554 $request = $client->get();
45555 $request->send();
45556
45557 $this->assertSame($response, $request->getResponse());
45558 $this->assertEquals(0, count($p));
45559 }
45560
45561 /**
45562 * @depends testAddsResponseFilesToQueue
45563 */
45564 public function testUpdateIgnoresWhenEmpty()
45565 {
45566 $p = new MockPlugin();
45567 $p->onRequestBeforeSend(new Event());
45568 }
45569
45570 /**
45571 * @depends testAddsMockResponseToRequestFromClient
45572 */
45573 public function testDetachesTemporaryWhenEmpty()
45574 {
45575 $p = new MockPlugin(null, true);
45576 $p->addResponse(MockPlugin::getMockFile(__DIR__ . '/../../TestData/mock_response'));
45577 $client = new Client('http://localhost:123/');
45578 $client->getEventDispatcher()->addSubscriber($p, 9999);
45579 $request = $client->get();
45580 $request->send();
45581
45582 $this->assertFalse($this->hasSubscriber($client, $p));
45583 }
45584
45585 public function testLoadsResponsesFromConstructor()
45586 {
45587 $p = new MockPlugin(array(new Response(200)));
45588 $this->assertEquals(1, $p->count());
45589 }
45590
45591 public function testStoresMockedRequests()
45592 {
45593 $p = new MockPlugin(array(new Response(200), new Response(200)));
45594 $client = new Client('http://localhost:123/');
45595 $client->getEventDispatcher()->addSubscriber($p, 9999);
45596
45597 $request1 = $client->get();
45598 $request1->send();
45599 $this->assertEquals(array($request1), $p->getReceivedRequests());
45600
45601 $request2 = $client->get();
45602 $request2->send();
45603 $this->assertEquals(array($request1, $request2), $p->getReceivedRequests());
45604
45605 $p->flush();
45606 $this->assertEquals(array(), $p->getReceivedRequests());
45607 }
45608
45609 public function testReadsBodiesFromMockedRequests()
45610 {
45611 $p = new MockPlugin(array(new Response(200)));
45612 $p->readBodies(true);
45613 $client = new Client('http://localhost:123/');
45614 $client->getEventDispatcher()->addSubscriber($p, 9999);
45615
45616 $body = EntityBody::factory('foo');
45617 $request = $client->put();
45618 $request->setBody($body);
45619 $request->send();
45620 $this->assertEquals(3, $body->ftell());
45621 }
45622
45623 public function testCanMockBadRequestExceptions()
45624 {
45625 $client = new Client('http://localhost:123/');
45626 $ex = new CurlException('Foo');
45627 $mock = new MockPlugin(array($ex));
45628 $client->addSubscriber($mock);
45629 $request = $client->get('foo');
45630
45631 try {
45632 $request->send();
45633 $this->fail('Did not dequeue an exception');
45634 } catch (CurlException $e) {
45635 $this->assertSame($e, $ex);
45636 $this->assertSame($request, $ex->getRequest());
45637 }
45638 }
45639 }
45640 <?php
45641
45642 namespace Guzzle\Tests\Plugin\ErrorResponse;
45643
45644 use Guzzle\Service\Client;
45645 use Guzzle\Http\Message\Response;
45646 use Guzzle\Plugin\ErrorResponse\ErrorResponsePlugin;
45647 use Guzzle\Service\Description\ServiceDescription;
45648 use Guzzle\Tests\Mock\ErrorResponseMock;
45649
45650 /**
45651 * @covers \Guzzle\Plugin\ErrorResponse\ErrorResponsePlugin
45652 */
45653 class ErrorResponsePluginTest extends \Guzzle\Tests\GuzzleTestCase
45654 {
45655 protected $client;
45656
45657 public static function tearDownAfterClass()
45658 {
45659 self::getServer()->flush();
45660 }
45661
45662 public function setUp()
45663 {
45664 $mockError = 'Guzzle\Tests\Mock\ErrorResponseMock';
45665 $description = ServiceDescription::factory(array(
45666 'operations' => array(
45667 'works' => array(
45668 'httpMethod' => 'GET',
45669 'errorResponses' => array(
45670 array('code' => 500, 'class' => $mockError),
45671 array('code' => 503, 'reason' => 'foo', 'class' => $mockError),
45672 array('code' => 200, 'reason' => 'Error!', 'class' => $mockError)
45673 )
45674 ),
45675 'bad_class' => array(
45676 'httpMethod' => 'GET',
45677 'errorResponses' => array(
45678 array('code' => 500, 'class' => 'Does\\Not\\Exist')
45679 )
45680 ),
45681 'does_not_implement' => array(
45682 'httpMethod' => 'GET',
45683 'errorResponses' => array(
45684 array('code' => 500, 'class' => __CLASS__)
45685 )
45686 ),
45687 'no_errors' => array('httpMethod' => 'GET'),
45688 'no_class' => array(
45689 'httpMethod' => 'GET',
45690 'errorResponses' => array(
45691 array('code' => 500)
45692 )
45693 ),
45694 )
45695 ));
45696 $this->client = new Client($this->getServer()->getUrl());
45697 $this->client->setDescription($description);
45698 }
45699
45700 /**
45701 * @expectedException \Guzzle\Http\Exception\ServerErrorResponseException
45702 */
45703 public function testSkipsWhenErrorResponsesIsNotSet()
45704 {
45705 $this->getServer()->enqueue("HTTP/1.1 500 Foo\r\nContent-Length: 0\r\n\r\n");
45706 $this->client->addSubscriber(new ErrorResponsePlugin());
45707 $this->client->getCommand('no_errors')->execute();
45708 }
45709
45710 public function testSkipsWhenErrorResponsesIsNotSetAndAllowsSuccess()
45711 {
45712 $this->getServer()->enqueue("HTTP/1.1 200 OK\r\nContent-Length: 0\r\n\r\n");
45713 $this->client->addSubscriber(new ErrorResponsePlugin());
45714 $this->client->getCommand('no_errors')->execute();
45715 }
45716
45717 /**
45718 * @expectedException \Guzzle\Plugin\ErrorResponse\Exception\ErrorResponseException
45719 * @expectedExceptionMessage Does\Not\Exist does not exist
45720 */
45721 public function testEnsuresErrorResponseExists()
45722 {
45723 $this->getServer()->enqueue("HTTP/1.1 500 Foo\r\nContent-Length: 0\r\n\r\n");
45724 $this->client->addSubscriber(new ErrorResponsePlugin());
45725 $this->client->getCommand('bad_class')->execute();
45726 }
45727
45728 /**
45729 * @expectedException \Guzzle\Plugin\ErrorResponse\Exception\ErrorResponseException
45730 * @expectedExceptionMessage must implement Guzzle\Plugin\ErrorResponse\ErrorResponseExceptionInterface
45731 */
45732 public function testEnsuresErrorResponseImplementsInterface()
45733 {
45734 $this->getServer()->enqueue("HTTP/1.1 500 Foo\r\nContent-Length: 0\r\n\r\n");
45735 $this->client->addSubscriber(new ErrorResponsePlugin());
45736 $this->client->getCommand('does_not_implement')->execute();
45737 }
45738
45739 public function testThrowsSpecificErrorResponseOnMatch()
45740 {
45741 try {
45742 $this->getServer()->enqueue("HTTP/1.1 500 Foo\r\nContent-Length: 0\r\n\r\n");
45743 $this->client->addSubscriber(new ErrorResponsePlugin());
45744 $command = $this->client->getCommand('works');
45745 $command->execute();
45746 $this->fail('Exception not thrown');
45747 } catch (ErrorResponseMock $e) {
45748 $this->assertSame($command, $e->command);
45749 $this->assertEquals(500, $e->response->getStatusCode());
45750 }
45751 }
45752
45753 /**
45754 * @expectedException \Guzzle\Tests\Mock\ErrorResponseMock
45755 */
45756 public function testThrowsWhenCodeAndPhraseMatch()
45757 {
45758 $this->getServer()->enqueue("HTTP/1.1 200 Error!\r\nContent-Length: 0\r\n\r\n");
45759 $this->client->addSubscriber(new ErrorResponsePlugin());
45760 $this->client->getCommand('works')->execute();
45761 }
45762
45763 public function testSkipsWhenReasonDoesNotMatch()
45764 {
45765 $this->getServer()->enqueue("HTTP/1.1 200 OK\r\nContent-Length: 0\r\n\r\n");
45766 $this->client->addSubscriber(new ErrorResponsePlugin());
45767 $this->client->getCommand('works')->execute();
45768 }
45769
45770 public function testSkipsWhenNoClassIsSet()
45771 {
45772 $this->getServer()->enqueue("HTTP/1.1 200 OK\r\nContent-Length: 0\r\n\r\n");
45773 $this->client->addSubscriber(new ErrorResponsePlugin());
45774 $this->client->getCommand('no_class')->execute();
45775 }
45776 }
45777 <?php
45778
45779 namespace Guzzle\Tests\Plugin\History;
45780
45781 use Guzzle\Http\Client;
45782 use Guzzle\Http\Message\Request;
45783 use Guzzle\Http\Message\Response;
45784 use Guzzle\Plugin\History\HistoryPlugin;
45785 use Guzzle\Plugin\Mock\MockPlugin;
45786
45787 /**
45788 * @covers Guzzle\Plugin\History\HistoryPlugin
45789 */
45790 class HistoryPluginTest extends \Guzzle\Tests\GuzzleTestCase
45791 {
45792 /**
45793 * Adds multiple requests to a plugin
45794 *
45795 * @param HistoryPlugin $h Plugin
45796 * @param int $num Number of requests to add
45797 *
45798 * @return array
45799 */
45800 protected function addRequests(HistoryPlugin $h, $num)
45801 {
45802 $requests = array();
45803 $client = new Client('http://localhost/');
45804 for ($i = 0; $i < $num; $i++) {
45805 $requests[$i] = $client->get();
45806 $requests[$i]->setResponse(new Response(200), true);
45807 $requests[$i]->send();
45808 $h->add($requests[$i]);
45809 }
45810
45811 return $requests;
45812 }
45813
45814 public function testDescribesSubscribedEvents()
45815 {
45816 $this->assertInternalType('array', HistoryPlugin::getSubscribedEvents());
45817 }
45818
45819 public function testMaintainsLimitValue()
45820 {
45821 $h = new HistoryPlugin();
45822 $this->assertSame($h, $h->setLimit(10));
45823 $this->assertEquals(10, $h->getLimit());
45824 }
45825
45826 public function testAddsRequests()
45827 {
45828 $h = new HistoryPlugin();
45829 $requests = $this->addRequests($h, 1);
45830 $this->assertEquals(1, count($h));
45831 $i = $h->getIterator();
45832 $this->assertEquals(1, count($i));
45833 $this->assertEquals($requests[0], $i[0]);
45834 }
45835
45836 /**
45837 * @depends testAddsRequests
45838 */
45839 public function testMaintainsLimit()
45840 {
45841 $h = new HistoryPlugin();
45842 $h->setLimit(2);
45843 $requests = $this->addRequests($h, 3);
45844 $this->assertEquals(2, count($h));
45845 $i = 0;
45846 foreach ($h as $request) {
45847 if ($i > 0) {
45848 $this->assertSame($requests[$i], $request);
45849 }
45850 }
45851 }
45852
45853 public function testReturnsLastRequest()
45854 {
45855 $h = new HistoryPlugin();
45856 $requests = $this->addRequests($h, 5);
45857 $this->assertSame(end($requests), $h->getLastRequest());
45858 }
45859
45860 public function testReturnsLastResponse()
45861 {
45862 $h = new HistoryPlugin();
45863 $requests = $this->addRequests($h, 5);
45864 $this->assertSame(end($requests)->getResponse(), $h->getLastResponse());
45865 }
45866
45867 public function testClearsHistory()
45868 {
45869 $h = new HistoryPlugin();
45870 $requests = $this->addRequests($h, 5);
45871 $this->assertEquals(5, count($h));
45872 $h->clear();
45873 $this->assertEquals(0, count($h));
45874 }
45875
45876 /**
45877 * @depends testAddsRequests
45878 */
45879 public function testUpdatesAddRequests()
45880 {
45881 $h = new HistoryPlugin();
45882 $client = new Client('http://localhost/');
45883 $client->getEventDispatcher()->addSubscriber($h);
45884
45885 $request = $client->get();
45886 $request->setResponse(new Response(200), true);
45887 $request->send();
45888
45889 $this->assertSame($request, $h->getLastRequest());
45890 }
45891
45892 public function testCanCastToString()
45893 {
45894 $client = new Client('http://localhost/');
45895 $h = new HistoryPlugin();
45896 $client->getEventDispatcher()->addSubscriber($h);
45897
45898 $mock = new MockPlugin(array(
45899 new Response(301, array('Location' => '/redirect1', 'Content-Length' => 0)),
45900 new Response(307, array('Location' => '/redirect2', 'Content-Length' => 0)),
45901 new Response(200, array('Content-Length' => '2'), 'HI')
45902 ));
45903
45904 $client->getEventDispatcher()->addSubscriber($mock);
45905 $request = $client->get();
45906 $request->send();
45907 $this->assertEquals(3, count($h));
45908 $this->assertEquals(3, count($mock->getReceivedRequests()));
45909
45910 $h = str_replace("\r", '', $h);
45911 $this->assertContains("> GET / HTTP/1.1\nHost: localhost\nUser-Agent:", $h);
45912 $this->assertContains("< HTTP/1.1 301 Moved Permanently\nLocation: /redirect1", $h);
45913 $this->assertContains("< HTTP/1.1 307 Temporary Redirect\nLocation: /redirect2", $h);
45914 $this->assertContains("< HTTP/1.1 200 OK\nContent-Length: 2\n\nHI", $h);
45915 }
45916 }
45917 <?php
45918
45919 namespace Guzzle\Tests\Common;
45920
45921 use Guzzle\Common\Collection;
45922 use Guzzle\Common\Exception\InvalidArgumentException;
45923 use Guzzle\Http\QueryString;
45924
45925 /**
45926 * @covers Guzzle\Common\Collection
45927 */
45928 class CollectionTest extends \Guzzle\Tests\GuzzleTestCase
45929 {
45930 /** @var Collection */
45931 protected $coll;
45932
45933 protected function setUp()
45934 {
45935 $this->coll = new Collection();
45936 }
45937
45938 public function testConstructorCanBeCalledWithNoParams()
45939 {
45940 $this->coll = new Collection();
45941 $p = $this->coll->getAll();
45942 $this->assertEmpty($p, '-> Collection must be empty when no data is passed');
45943 }
45944
45945 public function testConstructorCanBeCalledWithParams()
45946 {
45947 $testData = array(
45948 'test' => 'value',
45949 'test_2' => 'value2'
45950 );
45951 $this->coll = new Collection($testData);
45952 $this->assertEquals($this->coll->getAll(), $testData, '-> getAll() must return the data passed in the constructor');
45953 $this->assertEquals($this->coll->getAll(), $this->coll->toArray());
45954 }
45955
45956 public function testImplementsIteratorAggregate()
45957 {
45958 $this->coll->set('key', 'value');
45959 $this->assertInstanceOf('ArrayIterator', $this->coll->getIterator());
45960 $this->assertEquals(1, count($this->coll));
45961 $total = 0;
45962 foreach ($this->coll as $key => $value) {
45963 $this->assertEquals('key', $key);
45964 $this->assertEquals('value', $value);
45965 $total++;
45966 }
45967 $this->assertEquals(1, $total);
45968 }
45969
45970 public function testCanAddValuesToExistingKeysByUsingArray()
45971 {
45972 $this->coll->add('test', 'value1');
45973 $this->assertEquals($this->coll->getAll(), array('test' => 'value1'));
45974 $this->coll->add('test', 'value2');
45975 $this->assertEquals($this->coll->getAll(), array('test' => array('value1', 'value2')));
45976 $this->coll->add('test', 'value3');
45977 $this->assertEquals($this->coll->getAll(), array('test' => array('value1', 'value2', 'value3')));
45978 }
45979
45980 public function testHandlesMergingInDisparateDataSources()
45981 {
45982 $params = array(
45983 'test' => 'value1',
45984 'test2' => 'value2',
45985 'test3' => array('value3', 'value4')
45986 );
45987 $this->coll->merge($params);
45988 $this->assertEquals($this->coll->getAll(), $params);
45989
45990 // Pass the same object to itself
45991 $this->assertEquals($this->coll->merge($this->coll), $this->coll);
45992 }
45993
45994 public function testCanClearAllDataOrSpecificKeys()
45995 {
45996 $this->coll->merge(array(
45997 'test' => 'value1',
45998 'test2' => 'value2'
45999 ));
46000
46001 // Clear a specific parameter by name
46002 $this->coll->remove('test');
46003
46004 $this->assertEquals($this->coll->getAll(), array(
46005 'test2' => 'value2'
46006 ));
46007
46008 // Clear all parameters
46009 $this->coll->clear();
46010
46011 $this->assertEquals($this->coll->getAll(), array());
46012 }
46013
46014 public function testGetsValuesByKey()
46015 {
46016 $this->assertNull($this->coll->get('test'));
46017 $this->coll->add('test', 'value');
46018 $this->assertEquals('value', $this->coll->get('test'));
46019 $this->coll->set('test2', 'v2');
46020 $this->coll->set('test3', 'v3');
46021 $this->assertEquals(array(
46022 'test' => 'value',
46023 'test2' => 'v2'
46024 ), $this->coll->getAll(array('test', 'test2')));
46025 }
46026
46027 public function testProvidesKeys()
46028 {
46029 $this->assertEquals(array(), $this->coll->getKeys());
46030 $this->coll->merge(array(
46031 'test1' => 'value1',
46032 'test2' => 'value2'
46033 ));
46034 $this->assertEquals(array('test1', 'test2'), $this->coll->getKeys());
46035 // Returns the cached array previously returned
46036 $this->assertEquals(array('test1', 'test2'), $this->coll->getKeys());
46037 $this->coll->remove('test1');
46038 $this->assertEquals(array('test2'), $this->coll->getKeys());
46039 $this->coll->add('test3', 'value3');
46040 $this->assertEquals(array('test2', 'test3'), $this->coll->getKeys());
46041 }
46042
46043 public function testChecksIfHasKey()
46044 {
46045 $this->assertFalse($this->coll->hasKey('test'));
46046 $this->coll->add('test', 'value');
46047 $this->assertEquals(true, $this->coll->hasKey('test'));
46048 $this->coll->add('test2', 'value2');
46049 $this->assertEquals(true, $this->coll->hasKey('test'));
46050 $this->assertEquals(true, $this->coll->hasKey('test2'));
46051 $this->assertFalse($this->coll->hasKey('testing'));
46052 $this->assertEquals(false, $this->coll->hasKey('AB-C', 'junk'));
46053 }
46054
46055 public function testChecksIfHasValue()
46056 {
46057 $this->assertFalse($this->coll->hasValue('value'));
46058 $this->coll->add('test', 'value');
46059 $this->assertEquals('test', $this->coll->hasValue('value'));
46060 $this->coll->add('test2', 'value2');
46061 $this->assertEquals('test', $this->coll->hasValue('value'));
46062 $this->assertEquals('test2', $this->coll->hasValue('value2'));
46063 $this->assertFalse($this->coll->hasValue('val'));
46064 }
46065
46066 public function testCanGetAllValuesByArray()
46067 {
46068 $this->coll->add('foo', 'bar');
46069 $this->coll->add('tEsT', 'value');
46070 $this->coll->add('tesTing', 'v2');
46071 $this->coll->add('key', 'v3');
46072 $this->assertNull($this->coll->get('test'));
46073 $this->assertEquals(array(
46074 'foo' => 'bar',
46075 'tEsT' => 'value',
46076 'tesTing' => 'v2'
46077 ), $this->coll->getAll(array(
46078 'foo', 'tesTing', 'tEsT'
46079 )));
46080 }
46081
46082 public function testImplementsCount()
46083 {
46084 $data = new Collection();
46085 $this->assertEquals(0, $data->count());
46086 $data->add('key', 'value');
46087 $this->assertEquals(1, count($data));
46088 $data->add('key', 'value2');
46089 $this->assertEquals(1, count($data));
46090 $data->add('key_2', 'value3');
46091 $this->assertEquals(2, count($data));
46092 }
46093
46094 public function testAddParamsByMerging()
46095 {
46096 $params = array(
46097 'test' => 'value1',
46098 'test2' => 'value2',
46099 'test3' => array('value3', 'value4')
46100 );
46101
46102 // Add some parameters
46103 $this->coll->merge($params);
46104
46105 // Add more parameters by merging them in
46106 $this->coll->merge(array(
46107 'test' => 'another',
46108 'different_key' => 'new value'
46109 ));
46110
46111 $this->assertEquals(array(
46112 'test' => array('value1', 'another'),
46113 'test2' => 'value2',
46114 'test3' => array('value3', 'value4'),
46115 'different_key' => 'new value'
46116 ), $this->coll->getAll());
46117 }
46118
46119 public function testAllowsFunctionalFilter()
46120 {
46121 $this->coll->merge(array(
46122 'fruit' => 'apple',
46123 'number' => 'ten',
46124 'prepositions' => array('about', 'above', 'across', 'after'),
46125 'same_number' => 'ten'
46126 ));
46127
46128 $filtered = $this->coll->filter(function($key, $value) {
46129 return $value == 'ten';
46130 });
46131
46132 $this->assertNotEquals($filtered, $this->coll);
46133
46134 $this->assertEquals(array(
46135 'number' => 'ten',
46136 'same_number' => 'ten'
46137 ), $filtered->getAll());
46138 }
46139
46140 public function testAllowsFunctionalMapping()
46141 {
46142 $this->coll->merge(array(
46143 'number_1' => 1,
46144 'number_2' => 2,
46145 'number_3' => 3
46146 ));
46147
46148 $mapped = $this->coll->map(function($key, $value) {
46149 return $value * $value;
46150 });
46151
46152 $this->assertNotEquals($mapped, $this->coll);
46153
46154 $this->assertEquals(array(
46155 'number_1' => 1,
46156 'number_2' => 4,
46157 'number_3' => 9
46158 ), $mapped->getAll());
46159 }
46160
46161 public function testImplementsArrayAccess()
46162 {
46163 $this->coll->merge(array(
46164 'k1' => 'v1',
46165 'k2' => 'v2'
46166 ));
46167
46168 $this->assertTrue($this->coll->offsetExists('k1'));
46169 $this->assertFalse($this->coll->offsetExists('Krull'));
46170
46171 $this->coll->offsetSet('k3', 'v3');
46172 $this->assertEquals('v3', $this->coll->offsetGet('k3'));
46173 $this->assertEquals('v3', $this->coll->get('k3'));
46174
46175 $this->coll->offsetUnset('k1');
46176 $this->assertFalse($this->coll->offsetExists('k1'));
46177 }
46178
46179 public function testUsesStaticWhenCreatingNew()
46180 {
46181 $qs = new QueryString(array(
46182 'a' => 'b',
46183 'c' => 'd'
46184 ));
46185
46186 $this->assertInstanceOf('Guzzle\\Http\\QueryString', $qs->map(function($a, $b) {}));
46187 $this->assertInstanceOf('Guzzle\\Common\\Collection', $qs->map(function($a, $b) {}, array(), false));
46188
46189 $this->assertInstanceOf('Guzzle\\Http\\QueryString', $qs->filter(function($a, $b) {}));
46190 $this->assertInstanceOf('Guzzle\\Common\\Collection', $qs->filter(function($a, $b) {}, false));
46191 }
46192
46193 public function testCanReplaceAllData()
46194 {
46195 $this->assertSame($this->coll, $this->coll->replace(array(
46196 'a' => '123'
46197 )));
46198
46199 $this->assertEquals(array(
46200 'a' => '123'
46201 ), $this->coll->getAll());
46202 }
46203
46204 public function dataProvider()
46205 {
46206 return array(
46207 array('this_is_a_test', '{a}_is_a_{b}', array(
46208 'a' => 'this',
46209 'b' => 'test'
46210 )),
46211 array('this_is_a_test', '{abc}_is_a_{0}', array(
46212 'abc' => 'this',
46213 0 => 'test'
46214 )),
46215 array('this_is_a_test', '{abc}_is_a_{0}', array(
46216 'abc' => 'this',
46217 0 => 'test'
46218 )),
46219 array('this_is_a_test', 'this_is_a_test', array(
46220 'abc' => 'this'
46221 )),
46222 array('{abc}_is_{not_found}a_{0}', '{abc}_is_{not_found}a_{0}', array())
46223 );
46224 }
46225
46226 /**
46227 * @dataProvider dataProvider
46228 */
46229 public function testInjectsConfigData($output, $input, $config)
46230 {
46231 $collection = new Collection($config);
46232 $this->assertEquals($output, $collection->inject($input));
46233 }
46234
46235 public function testCanSearchByKey()
46236 {
46237 $collection = new Collection(array(
46238 'foo' => 'bar',
46239 'BaZ' => 'pho'
46240 ));
46241
46242 $this->assertEquals('foo', $collection->keySearch('FOO'));
46243 $this->assertEquals('BaZ', $collection->keySearch('baz'));
46244 $this->assertEquals(false, $collection->keySearch('Bar'));
46245 }
46246
46247 public function testPreparesFromConfig()
46248 {
46249 $c = Collection::fromConfig(array(
46250 'a' => '123',
46251 'base_url' => 'http://www.test.com/'
46252 ), array(
46253 'a' => 'xyz',
46254 'b' => 'lol'
46255 ), array('a'));
46256
46257 $this->assertInstanceOf('Guzzle\Common\Collection', $c);
46258 $this->assertEquals(array(
46259 'a' => '123',
46260 'b' => 'lol',
46261 'base_url' => 'http://www.test.com/'
46262 ), $c->getAll());
46263
46264 try {
46265 $c = Collection::fromConfig(array(), array(), array('a'));
46266 $this->fail('Exception not throw when missing config');
46267 } catch (InvalidArgumentException $e) {
46268 }
46269 }
46270
46271 function falseyDataProvider()
46272 {
46273 return array(
46274 array(false, false),
46275 array(null, null),
46276 array('', ''),
46277 array(array(), array()),
46278 array(0, 0),
46279 );
46280 }
46281
46282 /**
46283 * @dataProvider falseyDataProvider
46284 */
46285 public function testReturnsCorrectData($a, $b)
46286 {
46287 $c = new Collection(array('value' => $a));
46288 $this->assertSame($b, $c->get('value'));
46289 }
46290
46291 public function testRetrievesNestedKeysUsingPath()
46292 {
46293 $data = array(
46294 'foo' => 'bar',
46295 'baz' => array(
46296 'mesa' => array(
46297 'jar' => 'jar'
46298 )
46299 )
46300 );
46301 $collection = new Collection($data);
46302 $this->assertEquals('bar', $collection->getPath('foo'));
46303 $this->assertEquals('jar', $collection->getPath('baz/mesa/jar'));
46304 $this->assertNull($collection->getPath('wewewf'));
46305 $this->assertNull($collection->getPath('baz/mesa/jar/jar'));
46306 }
46307
46308 public function testFalseyKeysStillDescend()
46309 {
46310 $collection = new Collection(array(
46311 '0' => array(
46312 'a' => 'jar'
46313 ),
46314 1 => 'other'
46315 ));
46316 $this->assertEquals('jar', $collection->getPath('0/a'));
46317 $this->assertEquals('other', $collection->getPath('1'));
46318 }
46319
46320 public function getPathProvider()
46321 {
46322 $data = array(
46323 'foo' => 'bar',
46324 'baz' => array(
46325 'mesa' => array(
46326 'jar' => 'jar',
46327 'array' => array('a', 'b', 'c')
46328 ),
46329 'bar' => array(
46330 'baz' => 'bam',
46331 'array' => array('d', 'e', 'f')
46332 )
46333 ),
46334 'bam' => array(
46335 array('foo' => 1),
46336 array('foo' => 2),
46337 array('array' => array('h', 'i'))
46338 )
46339 );
46340 $c = new Collection($data);
46341
46342 return array(
46343 // Simple path selectors
46344 array($c, 'foo', 'bar'),
46345 array($c, 'baz', $data['baz']),
46346 array($c, 'bam', $data['bam']),
46347 array($c, 'baz/mesa', $data['baz']['mesa']),
46348 array($c, 'baz/mesa/jar', 'jar'),
46349 // Merge everything two levels under baz
46350 array($c, 'baz/*', array(
46351 'jar' => 'jar',
46352 'array' => array_merge($data['baz']['mesa']['array'], $data['baz']['bar']['array']),
46353 'baz' => 'bam'
46354 )),
46355 // Does not barf on missing keys
46356 array($c, 'fefwfw', null),
46357 // Does not barf when a wildcard does not resolve correctly
46358 array($c, '*/*/*/*/*/wefwfe', array()),
46359 // Allows custom separator
46360 array($c, '*|mesa', $data['baz']['mesa'], '|'),
46361 // Merge all 'array' keys two levels under baz (the trailing * does not hurt the results)
46362 array($c, 'baz/*/array/*', array_merge($data['baz']['mesa']['array'], $data['baz']['bar']['array'])),
46363 // Merge all 'array' keys two levels under baz
46364 array($c, 'baz/*/array', array_merge($data['baz']['mesa']['array'], $data['baz']['bar']['array'])),
46365 array($c, 'baz/mesa/array', $data['baz']['mesa']['array']),
46366 // Having a trailing * does not hurt the results
46367 array($c, 'baz/mesa/array/*', $data['baz']['mesa']['array']),
46368 // Merge of anything one level deep
46369 array($c, '*', array_merge(array('bar'), $data['baz'], $data['bam'])),
46370 // Funky merge of anything two levels deep
46371 array($c, '*/*', array(
46372 'jar' => 'jar',
46373 'array' => array('a', 'b', 'c', 'd', 'e', 'f', 'h', 'i'),
46374 'baz' => 'bam',
46375 'foo' => array(1, 2)
46376 )),
46377 // Funky merge of all 'array' keys that are two levels deep
46378 array($c, '*/*/array', array('a', 'b', 'c', 'd', 'e', 'f', 'h', 'i'))
46379 );
46380 }
46381
46382 /**
46383 * @dataProvider getPathProvider
46384 */
46385 public function testGetPath(Collection $c, $path, $expected, $separator = '/')
46386 {
46387 $this->assertEquals($expected, $c->getPath($path, $separator));
46388 }
46389
46390 public function testOverridesSettings()
46391 {
46392 $c = new Collection(array('foo' => 1, 'baz' => 2, 'bar' => 3));
46393 $c->overwriteWith(array('foo' => 10, 'bar' => 300));
46394 $this->assertEquals(array('foo' => 10, 'baz' => 2, 'bar' => 300), $c->getAll());
46395 }
46396
46397 public function testOverwriteWithCollection()
46398 {
46399 $c = new Collection(array('foo' => 1, 'baz' => 2, 'bar' => 3));
46400 $b = new Collection(array('foo' => 10, 'bar' => 300));
46401 $c->overwriteWith($b);
46402 $this->assertEquals(array('foo' => 10, 'baz' => 2, 'bar' => 300), $c->getAll());
46403 }
46404
46405 public function testOverwriteWithTraversable()
46406 {
46407 $c = new Collection(array('foo' => 1, 'baz' => 2, 'bar' => 3));
46408 $b = new Collection(array('foo' => 10, 'bar' => 300));
46409 $c->overwriteWith($b->getIterator());
46410 $this->assertEquals(array('foo' => 10, 'baz' => 2, 'bar' => 300), $c->getAll());
46411 }
46412
46413 public function testCanSetNestedPathValueThatDoesNotExist()
46414 {
46415 $c = new Collection(array());
46416 $c->setPath('foo/bar/baz/123', 'hi');
46417 $this->assertEquals('hi', $c['foo']['bar']['baz']['123']);
46418 }
46419
46420 public function testCanSetNestedPathValueThatExists()
46421 {
46422 $c = new Collection(array('foo' => array('bar' => 'test')));
46423 $c->setPath('foo/bar', 'hi');
46424 $this->assertEquals('hi', $c['foo']['bar']);
46425 }
46426
46427 /**
46428 * @expectedException \Guzzle\Common\Exception\RuntimeException
46429 */
46430 public function testVerifiesNestedPathIsValidAtExactLevel()
46431 {
46432 $c = new Collection(array('foo' => 'bar'));
46433 $c->setPath('foo/bar', 'hi');
46434 $this->assertEquals('hi', $c['foo']['bar']);
46435 }
46436
46437 /**
46438 * @expectedException \Guzzle\Common\Exception\RuntimeException
46439 */
46440 public function testVerifiesThatNestedPathIsValidAtAnyLevel()
46441 {
46442 $c = new Collection(array('foo' => 'bar'));
46443 $c->setPath('foo/bar/baz', 'test');
46444 }
46445 }
46446 <?php
46447
46448 namespace Guzzle\Tests\Common;
46449
46450 use Guzzle\Common\Event;
46451
46452 /**
46453 * @covers Guzzle\Common\Event
46454 */
46455 class EventTest extends \Guzzle\Tests\GuzzleTestCase
46456 {
46457 /**
46458 * @return Event
46459 */
46460 private function getEvent()
46461 {
46462 return new Event(array(
46463 'test' => '123',
46464 'other' => '456',
46465 'event' => 'test.notify'
46466 ));
46467 }
46468
46469 public function testAllowsParameterInjection()
46470 {
46471 $event = new Event(array(
46472 'test' => '123'
46473 ));
46474 $this->assertEquals('123', $event['test']);
46475 }
46476
46477 public function testImplementsArrayAccess()
46478 {
46479 $event = $this->getEvent();
46480 $this->assertEquals('123', $event['test']);
46481 $this->assertNull($event['foobar']);
46482
46483 $this->assertTrue($event->offsetExists('test'));
46484 $this->assertFalse($event->offsetExists('foobar'));
46485
46486 unset($event['test']);
46487 $this->assertFalse($event->offsetExists('test'));
46488
46489 $event['test'] = 'new';
46490 $this->assertEquals('new', $event['test']);
46491 }
46492
46493 public function testImplementsIteratorAggregate()
46494 {
46495 $event = $this->getEvent();
46496 $this->assertInstanceOf('ArrayIterator', $event->getIterator());
46497 }
46498
46499 public function testConvertsToArray()
46500 {
46501 $this->assertEquals(array(
46502 'test' => '123',
46503 'other' => '456',
46504 'event' => 'test.notify'
46505 ), $this->getEvent()->toArray());
46506 }
46507 }
46508 <?php
46509
46510 namespace Guzzle\Tests\Common;
46511
46512 use Guzzle\Common\Event;
46513 use Guzzle\Common\AbstractHasDispatcher;
46514 use Symfony\Component\EventDispatcher\EventDispatcher;
46515
46516 /**
46517 * @covers Guzzle\Common\AbstractHasDispatcher
46518 */
46519 class AbstractHasAdapterTest extends \Guzzle\Tests\GuzzleTestCase
46520 {
46521 public function testDoesNotRequireRegisteredEvents()
46522 {
46523 $this->assertEquals(array(), AbstractHasDispatcher::getAllEvents());
46524 }
46525
46526 public function testAllowsDispatcherToBeInjected()
46527 {
46528 $d = new EventDispatcher();
46529 $mock = $this->getMockForAbstractClass('Guzzle\Common\AbstractHasDispatcher');
46530 $this->assertSame($mock, $mock->setEventDispatcher($d));
46531 $this->assertSame($d, $mock->getEventDispatcher());
46532 }
46533
46534 public function testCreatesDefaultEventDispatcherIfNeeded()
46535 {
46536 $mock = $this->getMockForAbstractClass('Guzzle\Common\AbstractHasDispatcher');
46537 $this->assertInstanceOf('Symfony\Component\EventDispatcher\EventDispatcher', $mock->getEventDispatcher());
46538 }
46539
46540 public function testHelperDispatchesEvents()
46541 {
46542 $data = array();
46543 $mock = $this->getMockForAbstractClass('Guzzle\Common\AbstractHasDispatcher');
46544 $mock->getEventDispatcher()->addListener('test', function(Event $e) use (&$data) {
46545 $data = $e->getIterator()->getArrayCopy();
46546 });
46547 $mock->dispatch('test', array(
46548 'param' => 'abc'
46549 ));
46550 $this->assertEquals(array(
46551 'param' => 'abc',
46552 ), $data);
46553 }
46554
46555 public function testHelperAttachesSubscribers()
46556 {
46557 $mock = $this->getMockForAbstractClass('Guzzle\Common\AbstractHasDispatcher');
46558 $subscriber = $this->getMockForAbstractClass('Symfony\Component\EventDispatcher\EventSubscriberInterface');
46559
46560 $dispatcher = $this->getMockBuilder('Symfony\Component\EventDispatcher\EventDispatcher')
46561 ->setMethods(array('addSubscriber'))
46562 ->getMock();
46563
46564 $dispatcher->expects($this->once())
46565 ->method('addSubscriber');
46566
46567 $mock->setEventDispatcher($dispatcher);
46568 $mock->addSubscriber($subscriber);
46569 }
46570 }
46571 <?php
46572
46573 namespace Guzzle\Tests\Common\Exception;
46574
46575 use Guzzle\Batch\Exception\BatchTransferException;
46576
46577 class BatchTransferExceptionTest extends \Guzzle\Tests\GuzzleTestCase
46578 {
46579 public function testContainsBatch()
46580 {
46581 $e = new \Exception('Baz!');
46582 $t = $this->getMock('Guzzle\Batch\BatchTransferInterface');
46583 $d = $this->getMock('Guzzle\Batch\BatchDivisorInterface');
46584 $transferException = new BatchTransferException(array('foo'), array(1, 2), $e, $t, $d);
46585 $this->assertEquals(array('foo'), $transferException->getBatch());
46586 $this->assertSame($t, $transferException->getTransferStrategy());
46587 $this->assertSame($d, $transferException->getDivisorStrategy());
46588 $this->assertSame($e, $transferException->getPrevious());
46589 $this->assertEquals(array(1, 2), $transferException->getTransferredItems());
46590 }
46591 }
46592 <?php
46593
46594 namespace Guzzle\Tests\Common\Exception;
46595
46596 use Guzzle\Common\Exception\ExceptionCollection;
46597
46598 class ExceptionCollectionTest extends \Guzzle\Tests\GuzzleTestCase
46599 {
46600 private function getExceptions()
46601 {
46602 return array(
46603 new \Exception('Test'),
46604 new \Exception('Testing')
46605 );
46606 }
46607
46608 public function testAggregatesExceptions()
46609 {
46610 $e = new ExceptionCollection();
46611 $exceptions = $this->getExceptions();
46612 $e->add($exceptions[0]);
46613 $e->add($exceptions[1]);
46614 $this->assertEquals("(Exception) Test\n(Exception) Testing", $e->getMessage());
46615 $this->assertSame($exceptions[0], $e->getFirst());
46616 }
46617
46618 public function testCanSetExceptions()
46619 {
46620 $ex = new \Exception('foo');
46621 $e = new ExceptionCollection();
46622 $e->setExceptions(array($ex));
46623 $this->assertSame($ex, $e->getFirst());
46624 }
46625
46626 public function testActsAsArray()
46627 {
46628 $e = new ExceptionCollection();
46629 $exceptions = $this->getExceptions();
46630 $e->add($exceptions[0]);
46631 $e->add($exceptions[1]);
46632 $this->assertEquals(2, count($e));
46633 $this->assertEquals($exceptions, $e->getIterator()->getArrayCopy());
46634 }
46635
46636 public function testCanAddSelf()
46637 {
46638 $e1 = new ExceptionCollection();
46639 $e1->add(new \Exception("Test"));
46640 $e2 = new ExceptionCollection('Meta description!');
46641 $e2->add(new \Exception("Test 2"));
46642 $e3 = new ExceptionCollection();
46643 $e3->add(new \Exception('Baz'));
46644 $e2->add($e3);
46645 $e1->add($e2);
46646 $message = $e1->getMessage();
46647 $this->assertEquals("(Exception) Test\n"
46648 . "(Guzzle\\Common\\Exception\\ExceptionCollection)\n"
46649 . " Meta description!\n"
46650 . " (Exception) Test 2\n"
46651 . " (Guzzle\\Common\\Exception\\ExceptionCollection)\n"
46652 . " (Exception) Baz", $message);
46653 }
46654 }
46655 <?php
46656
46657 namespace Guzzle\Tests\Common;
46658
46659 use Guzzle\Common\Version;
46660
46661 /**
46662 * @covers Guzzle\Common\Version
46663 */
46664 class VersionTest extends \Guzzle\Tests\GuzzleTestCase
46665 {
46666 /**
46667 * @expectedException \PHPUnit_Framework_Error_Deprecated
46668 */
46669 public function testEmitsWarnings()
46670 {
46671 Version::$emitWarnings = true;
46672 Version::warn('testing!');
46673 }
46674
46675 public function testCanSilenceWarnings()
46676 {
46677 Version::$emitWarnings = false;
46678 Version::warn('testing!');
46679 Version::$emitWarnings = true;
46680 }
46681 }
46682 <?php
46683
46684 namespace Guzzle\Tests\Service;
46685
46686 use Guzzle\Cache\DoctrineCacheAdapter;
46687 use Guzzle\Service\CachingConfigLoader;
46688 use Doctrine\Common\Cache\ArrayCache;
46689
46690 /**
46691 * @covers Guzzle\Service\CachingConfigLoader
46692 */
46693 class CachingConfigLoaderTest extends \Guzzle\Tests\GuzzleTestCase
46694 {
46695 public function testLoadsPhpFileIncludes()
46696 {
46697 $cache = new DoctrineCacheAdapter(new ArrayCache());
46698 $loader = $this->getMockBuilder('Guzzle\Service\ConfigLoaderInterface')
46699 ->setMethods(array('load'))
46700 ->getMockForAbstractClass();
46701 $data = array('foo' => 'bar');
46702 $loader->expects($this->once())
46703 ->method('load')
46704 ->will($this->returnValue($data));
46705 $cache = new CachingConfigLoader($loader, $cache);
46706 $this->assertEquals($data, $cache->load('foo'));
46707 $this->assertEquals($data, $cache->load('foo'));
46708 }
46709
46710 public function testDoesNotCacheArrays()
46711 {
46712 $cache = new DoctrineCacheAdapter(new ArrayCache());
46713 $loader = $this->getMockBuilder('Guzzle\Service\ConfigLoaderInterface')
46714 ->setMethods(array('load'))
46715 ->getMockForAbstractClass();
46716 $data = array('foo' => 'bar');
46717 $loader->expects($this->exactly(2))
46718 ->method('load')
46719 ->will($this->returnValue($data));
46720 $cache = new CachingConfigLoader($loader, $cache);
46721 $this->assertEquals($data, $cache->load(array()));
46722 $this->assertEquals($data, $cache->load(array()));
46723 }
46724 }
46725 <?php
46726
46727 namespace Guzzle\Tests\Service\Description;
46728
46729 use Guzzle\Service\Description\SchemaFormatter;
46730
46731 /**
46732 * @covers Guzzle\Service\Description\SchemaFormatter
46733 */
46734 class SchemaFormatterTest extends \Guzzle\Tests\GuzzleTestCase
46735 {
46736 public function dateTimeProvider()
46737 {
46738 $dateUtc = 'October 13, 2012 16:15:46 UTC';
46739 $dateOffset = 'October 13, 2012 10:15:46 -06:00';
46740 $expectedDateTime = '2012-10-13T16:15:46Z';
46741
46742 return array(
46743 array('foo', 'does-not-exist', 'foo'),
46744 array($dateUtc, 'date-time', $expectedDateTime),
46745 array($dateUtc, 'date-time-http', 'Sat, 13 Oct 2012 16:15:46 GMT'),
46746 array($dateUtc, 'date', '2012-10-13'),
46747 array($dateUtc, 'timestamp', strtotime($dateUtc)),
46748 array(new \DateTime($dateUtc), 'timestamp', strtotime($dateUtc)),
46749 array($dateUtc, 'time', '16:15:46'),
46750 array(strtotime($dateUtc), 'time', '16:15:46'),
46751 array(strtotime($dateUtc), 'timestamp', strtotime($dateUtc)),
46752 array('true', 'boolean-string', 'true'),
46753 array(true, 'boolean-string', 'true'),
46754 array('false', 'boolean-string', 'false'),
46755 array(false, 'boolean-string', 'false'),
46756 array('1350144946', 'date-time', $expectedDateTime),
46757 array(1350144946, 'date-time', $expectedDateTime),
46758 array($dateOffset, 'date-time', $expectedDateTime)
46759 );
46760 }
46761
46762 /**
46763 * @dataProvider dateTimeProvider
46764 */
46765 public function testFilters($value, $format, $result)
46766 {
46767 $this->assertEquals($result, SchemaFormatter::format($format, $value));
46768 }
46769
46770 /**
46771 * @expectedException \Guzzle\Common\Exception\InvalidArgumentException
46772 */
46773 public function testValidatesDateTimeInput()
46774 {
46775 SchemaFormatter::format('date-time', false);
46776 }
46777 }
46778 <?php
46779
46780 namespace Guzzle\Tests\Service\Description;
46781
46782 use Guzzle\Service\Description\Parameter;
46783 use Guzzle\Service\Description\ServiceDescription;
46784
46785 /**
46786 * @covers Guzzle\Service\Description\Parameter
46787 */
46788 class ParameterTest extends \Guzzle\Tests\GuzzleTestCase
46789 {
46790 protected $data = array(
46791 'name' => 'foo',
46792 'type' => 'bar',
46793 'required' => true,
46794 'default' => '123',
46795 'description' => '456',
46796 'minLength' => 2,
46797 'maxLength' => 5,
46798 'location' => 'body',
46799 'static' => 'static!',
46800 'filters' => array('trim', 'json_encode')
46801 );
46802
46803 public function testCreatesParamFromArray()
46804 {
46805 $p = new Parameter($this->data);
46806 $this->assertEquals('foo', $p->getName());
46807 $this->assertEquals('bar', $p->getType());
46808 $this->assertEquals(true, $p->getRequired());
46809 $this->assertEquals('123', $p->getDefault());
46810 $this->assertEquals('456', $p->getDescription());
46811 $this->assertEquals(2, $p->getMinLength());
46812 $this->assertEquals(5, $p->getMaxLength());
46813 $this->assertEquals('body', $p->getLocation());
46814 $this->assertEquals('static!', $p->getStatic());
46815 $this->assertEquals(array('trim', 'json_encode'), $p->getFilters());
46816 }
46817
46818 public function testCanConvertToArray()
46819 {
46820 $p = new Parameter($this->data);
46821 unset($this->data['name']);
46822 $this->assertEquals($this->data, $p->toArray());
46823 }
46824
46825 public function testUsesStatic()
46826 {
46827 $d = $this->data;
46828 $d['default'] = 'booboo';
46829 $d['static'] = true;
46830 $p = new Parameter($d);
46831 $this->assertEquals('booboo', $p->getValue('bar'));
46832 }
46833
46834 public function testUsesDefault()
46835 {
46836 $d = $this->data;
46837 $d['default'] = 'foo';
46838 $d['static'] = null;
46839 $p = new Parameter($d);
46840 $this->assertEquals('foo', $p->getValue(null));
46841 }
46842
46843 public function testReturnsYourValue()
46844 {
46845 $d = $this->data;
46846 $d['static'] = null;
46847 $p = new Parameter($d);
46848 $this->assertEquals('foo', $p->getValue('foo'));
46849 }
46850
46851 public function testFiltersValues()
46852 {
46853 $d = $this->data;
46854 $d['static'] = null;
46855 $d['filters'] = 'strtoupper';
46856 $p = new Parameter($d);
46857 $this->assertEquals('FOO', $p->filter('foo'));
46858 }
46859
46860 public function testConvertsBooleans()
46861 {
46862 $p = new Parameter(array('type' => 'boolean'));
46863 $this->assertEquals(true, $p->filter('true'));
46864 $this->assertEquals(false, $p->filter('false'));
46865 }
46866
46867 public function testUsesArrayByDefaultForFilters()
46868 {
46869 $d = $this->data;
46870 $d['filters'] = null;
46871 $p = new Parameter($d);
46872 $this->assertEquals(array(), $p->getFilters());
46873 }
46874
46875 public function testAllowsSimpleLocationValue()
46876 {
46877 $p = new Parameter(array('name' => 'myname', 'location' => 'foo', 'sentAs' => 'Hello'));
46878 $this->assertEquals('foo', $p->getLocation());
46879 $this->assertEquals('Hello', $p->getSentAs());
46880 }
46881
46882 public function testParsesTypeValues()
46883 {
46884 $p = new Parameter(array('type' => 'foo'));
46885 $this->assertEquals('foo', $p->getType());
46886 }
46887
46888 /**
46889 * @expectedException InvalidArgumentException
46890 * @expectedExceptionMessage A [method] value must be specified for each complex filter
46891 */
46892 public function testValidatesComplexFilters()
46893 {
46894 $p = new Parameter(array('filters' => array(array('args' => 'foo'))));
46895 }
46896
46897 public function testCanBuildUpParams()
46898 {
46899 $p = new Parameter(array());
46900 $p->setName('foo')
46901 ->setDescription('c')
46902 ->setFilters(array('d'))
46903 ->setLocation('e')
46904 ->setSentAs('f')
46905 ->setMaxLength(1)
46906 ->setMinLength(1)
46907 ->setMinimum(2)
46908 ->setMaximum(2)
46909 ->setMinItems(3)
46910 ->setMaxItems(3)
46911 ->setRequired(true)
46912 ->setStatic(true)
46913 ->setDefault('h')
46914 ->setType('i');
46915
46916 $p->addFilter('foo');
46917
46918 $this->assertEquals('foo', $p->getName());
46919 $this->assertEquals('h', $p->getDefault());
46920 $this->assertEquals('c', $p->getDescription());
46921 $this->assertEquals(array('d', 'foo'), $p->getFilters());
46922 $this->assertEquals('e', $p->getLocation());
46923 $this->assertEquals('f', $p->getSentAs());
46924 $this->assertEquals(1, $p->getMaxLength());
46925 $this->assertEquals(1, $p->getMinLength());
46926 $this->assertEquals(2, $p->getMaximum());
46927 $this->assertEquals(2, $p->getMinimum());
46928 $this->assertEquals(3, $p->getMaxItems());
46929 $this->assertEquals(3, $p->getMinItems());
46930 $this->assertEquals(true, $p->getRequired());
46931 $this->assertEquals(true, $p->getStatic());
46932 $this->assertEquals('i', $p->getType());
46933 }
46934
46935 public function testAllowsNestedShape()
46936 {
46937 $command = $this->getServiceBuilder()->get('mock')->getCommand('mock_command')->getOperation();
46938 $param = new Parameter(array(
46939 'parent' => $command,
46940 'name' => 'foo',
46941 'type' => 'object',
46942 'location' => 'query',
46943 'properties' => array(
46944 'foo' => array(
46945 'type' => 'object',
46946 'required' => true,
46947 'properties' => array(
46948 'baz' => array(
46949 'name' => 'baz',
46950 'type' => 'bool',
46951 )
46952 )
46953 ),
46954 'bar' => array(
46955 'name' => 'bar',
46956 'default' => '123'
46957 )
46958 )
46959 ));
46960
46961 $this->assertSame($command, $param->getParent());
46962 $this->assertNotEmpty($param->getProperties());
46963 $this->assertInstanceOf('Guzzle\Service\Description\Parameter', $param->getProperty('foo'));
46964 $this->assertSame($param, $param->getProperty('foo')->getParent());
46965 $this->assertSame($param->getProperty('foo'), $param->getProperty('foo')->getProperty('baz')->getParent());
46966 $this->assertInstanceOf('Guzzle\Service\Description\Parameter', $param->getProperty('bar'));
46967 $this->assertSame($param, $param->getProperty('bar')->getParent());
46968
46969 $array = $param->toArray();
46970 $this->assertInternalType('array', $array['properties']);
46971 $this->assertArrayHasKey('foo', $array['properties']);
46972 $this->assertArrayHasKey('bar', $array['properties']);
46973 }
46974
46975 public function testAllowsComplexFilters()
46976 {
46977 $that = $this;
46978 $param = new Parameter(array());
46979 $param->setFilters(array(array('method' => function ($a, $b, $c, $d) use ($that, $param) {
46980 $that->assertEquals('test', $a);
46981 $that->assertEquals('my_value!', $b);
46982 $that->assertEquals('bar', $c);
46983 $that->assertSame($param, $d);
46984 return 'abc' . $b;
46985 }, 'args' => array('test', '@value', 'bar', '@api'))));
46986 $this->assertEquals('abcmy_value!', $param->filter('my_value!'));
46987 }
46988
46989 public function testCanChangeParentOfNestedParameter()
46990 {
46991 $param1 = new Parameter(array('name' => 'parent'));
46992 $param2 = new Parameter(array('name' => 'child'));
46993 $param2->setParent($param1);
46994 $this->assertSame($param1, $param2->getParent());
46995 }
46996
46997 public function testCanRemoveFromNestedStructure()
46998 {
46999 $param1 = new Parameter(array('name' => 'parent'));
47000 $param2 = new Parameter(array('name' => 'child'));
47001 $param1->addProperty($param2);
47002 $this->assertSame($param1, $param2->getParent());
47003 $this->assertSame($param2, $param1->getProperty('child'));
47004
47005 // Remove a single child from the structure
47006 $param1->removeProperty('child');
47007 $this->assertNull($param1->getProperty('child'));
47008 // Remove the entire structure
47009 $param1->addProperty($param2);
47010 $param1->removeProperty('child');
47011 $this->assertNull($param1->getProperty('child'));
47012 }
47013
47014 public function testAddsAdditionalProperties()
47015 {
47016 $p = new Parameter(array(
47017 'type' => 'object',
47018 'additionalProperties' => array('type' => 'string')
47019 ));
47020 $this->assertInstanceOf('Guzzle\Service\Description\Parameter', $p->getAdditionalProperties());
47021 $this->assertNull($p->getAdditionalProperties()->getAdditionalProperties());
47022 $p = new Parameter(array('type' => 'object'));
47023 $this->assertTrue($p->getAdditionalProperties());
47024 }
47025
47026 public function testAddsItems()
47027 {
47028 $p = new Parameter(array(
47029 'type' => 'array',
47030 'items' => array('type' => 'string')
47031 ));
47032 $this->assertInstanceOf('Guzzle\Service\Description\Parameter', $p->getItems());
47033 $out = $p->toArray();
47034 $this->assertEquals('array', $out['type']);
47035 $this->assertInternalType('array', $out['items']);
47036 }
47037
47038 public function testHasExtraProperties()
47039 {
47040 $p = new Parameter();
47041 $this->assertEquals(array(), $p->getData());
47042 $p->setData(array('foo' => 'bar'));
47043 $this->assertEquals('bar', $p->getData('foo'));
47044 $p->setData('baz', 'boo');
47045 $this->assertEquals(array('foo' => 'bar', 'baz' => 'boo'), $p->getData());
47046 }
47047
47048 public function testCanRetrieveKnownPropertiesUsingDataMethod()
47049 {
47050 $p = new Parameter();
47051 $this->assertEquals(null, $p->getData('foo'));
47052 $p->setName('test');
47053 $this->assertEquals('test', $p->getData('name'));
47054 }
47055
47056 public function testHasInstanceOf()
47057 {
47058 $p = new Parameter();
47059 $this->assertNull($p->getInstanceOf());
47060 $p->setInstanceOf('Foo');
47061 $this->assertEquals('Foo', $p->getInstanceOf());
47062 }
47063
47064 public function testHasPattern()
47065 {
47066 $p = new Parameter();
47067 $this->assertNull($p->getPattern());
47068 $p->setPattern('/[0-9]+/');
47069 $this->assertEquals('/[0-9]+/', $p->getPattern());
47070 }
47071
47072 public function testHasEnum()
47073 {
47074 $p = new Parameter();
47075 $this->assertNull($p->getEnum());
47076 $p->setEnum(array('foo', 'bar'));
47077 $this->assertEquals(array('foo', 'bar'), $p->getEnum());
47078 }
47079
47080 public function testSerializesItems()
47081 {
47082 $p = new Parameter(array(
47083 'type' => 'object',
47084 'additionalProperties' => array('type' => 'string')
47085 ));
47086 $this->assertEquals(array(
47087 'type' => 'object',
47088 'additionalProperties' => array('type' => 'string')
47089 ), $p->toArray());
47090 }
47091
47092 public function testResolvesRefKeysRecursively()
47093 {
47094 $jarJar = array('type' => 'string', 'default' => 'Mesa address tha senate!');
47095 $anakin = array('type' => 'array', 'items' => array('$ref' => 'JarJar'));
47096 $description = new ServiceDescription(array(
47097 'models' => array(
47098 'JarJar' => $jarJar,
47099 'Anakin' => $anakin
47100 )
47101 ));
47102 // description attribute will be removed
47103 $p = new Parameter(array('$ref' => 'Anakin', 'description' => 'missing'), $description);
47104 $this->assertEquals(array(
47105 'type' => 'array',
47106 'items' => array(
47107 'type' => 'string',
47108 'default' => 'Mesa address tha senate!'
47109 )
47110 ), $p->toArray());
47111 }
47112
47113 public function testResolvesExtendsRecursively()
47114 {
47115 $jarJar = array('type' => 'string', 'default' => 'Mesa address tha senate!', 'description' => 'a');
47116 $anakin = array('type' => 'array', 'items' => array('extends' => 'JarJar', 'description' => 'b'));
47117 $description = new ServiceDescription(array(
47118 'models' => array('JarJar' => $jarJar, 'Anakin' => $anakin)
47119 ));
47120 // Description attribute will be updated, and format added
47121 $p = new Parameter(array('extends' => 'Anakin', 'format' => 'date'), $description);
47122 $this->assertEquals(array(
47123 'type' => 'array',
47124 'format' => 'date',
47125 'items' => array(
47126 'type' => 'string',
47127 'default' => 'Mesa address tha senate!',
47128 'description' => 'b'
47129 )
47130 ), $p->toArray());
47131 }
47132
47133 public function testHasKeyMethod()
47134 {
47135 $p = new Parameter(array('name' => 'foo', 'sentAs' => 'bar'));
47136 $this->assertEquals('bar', $p->getWireName());
47137 $p->setSentAs(null);
47138 $this->assertEquals('foo', $p->getWireName());
47139 }
47140
47141 public function testIncludesNameInToArrayWhenItemsAttributeHasName()
47142 {
47143 $p = new Parameter(array(
47144 'type' => 'array',
47145 'name' => 'Abc',
47146 'items' => array(
47147 'name' => 'Foo',
47148 'type' => 'object'
47149 )
47150 ));
47151 $result = $p->toArray();
47152 $this->assertEquals(array(
47153 'type' => 'array',
47154 'items' => array(
47155 'name' => 'Foo',
47156 'type' => 'object',
47157 'additionalProperties' => true
47158 )
47159 ), $result);
47160 }
47161
47162 public function dateTimeProvider()
47163 {
47164 $d = 'October 13, 2012 16:15:46 UTC';
47165
47166 return array(
47167 array($d, 'date-time', '2012-10-13T16:15:46Z'),
47168 array($d, 'date', '2012-10-13'),
47169 array($d, 'timestamp', strtotime($d)),
47170 array(new \DateTime($d), 'timestamp', strtotime($d))
47171 );
47172 }
47173
47174 /**
47175 * @dataProvider dateTimeProvider
47176 */
47177 public function testAppliesFormat($d, $format, $result)
47178 {
47179 $p = new Parameter();
47180 $p->setFormat($format);
47181 $this->assertEquals($format, $p->getFormat());
47182 $this->assertEquals($result, $p->filter($d));
47183 }
47184 }
47185 <?php
47186
47187 namespace Guzzle\Tests\Service\Description;
47188
47189 use Guzzle\Service\Description\ServiceDescription;
47190 use Guzzle\Service\Description\ServiceDescriptionLoader;
47191
47192 /**
47193 * @covers Guzzle\Service\Description\ServiceDescriptionLoader
47194 */
47195 class ServiceDescriptionLoaderTest extends \Guzzle\Tests\GuzzleTestCase
47196 {
47197 public function testAllowsExtraData()
47198 {
47199 $d = ServiceDescription::factory(array(
47200 'foo' => true,
47201 'baz' => array('bar'),
47202 'apiVersion' => '123',
47203 'operations' => array()
47204 ));
47205
47206 $this->assertEquals(true, $d->getData('foo'));
47207 $this->assertEquals(array('bar'), $d->getData('baz'));
47208 $this->assertEquals('123', $d->getApiVersion());
47209 }
47210
47211 public function testAllowsDeepNestedInheritance()
47212 {
47213 $d = ServiceDescription::factory(array(
47214 'operations' => array(
47215 'abstract' => array(
47216 'httpMethod' => 'HEAD',
47217 'parameters' => array(
47218 'test' => array('type' => 'string', 'required' => true)
47219 )
47220 ),
47221 'abstract2' => array('uri' => '/test', 'extends' => 'abstract'),
47222 'concrete' => array('extends' => 'abstract2'),
47223 'override' => array('extends' => 'abstract', 'httpMethod' => 'PUT'),
47224 'override2' => array('extends' => 'override', 'httpMethod' => 'POST', 'uri' => '/')
47225 )
47226 ));
47227
47228 $c = $d->getOperation('concrete');
47229 $this->assertEquals('/test', $c->getUri());
47230 $this->assertEquals('HEAD', $c->getHttpMethod());
47231 $params = $c->getParams();
47232 $param = $params['test'];
47233 $this->assertEquals('string', $param->getType());
47234 $this->assertTrue($param->getRequired());
47235
47236 // Ensure that merging HTTP method does not make an array
47237 $this->assertEquals('PUT', $d->getOperation('override')->getHttpMethod());
47238 $this->assertEquals('POST', $d->getOperation('override2')->getHttpMethod());
47239 $this->assertEquals('/', $d->getOperation('override2')->getUri());
47240 }
47241
47242 /**
47243 * @expectedException RuntimeException
47244 */
47245 public function testThrowsExceptionWhenExtendingMissingCommand()
47246 {
47247 ServiceDescription::factory(array(
47248 'operations' => array(
47249 'concrete' => array(
47250 'extends' => 'missing'
47251 )
47252 )
47253 ));
47254 }
47255
47256 public function testAllowsMultipleInheritance()
47257 {
47258 $description = ServiceDescription::factory(array(
47259 'operations' => array(
47260 'a' => array(
47261 'httpMethod' => 'GET',
47262 'parameters' => array(
47263 'a1' => array(
47264 'default' => 'foo',
47265 'required' => true,
47266 'prepend' => 'hi'
47267 )
47268 )
47269 ),
47270 'b' => array(
47271 'extends' => 'a',
47272 'parameters' => array(
47273 'b2' => array()
47274 )
47275 ),
47276 'c' => array(
47277 'parameters' => array(
47278 'a1' => array(
47279 'default' => 'bar',
47280 'required' => true,
47281 'description' => 'test'
47282 ),
47283 'c3' => array()
47284 )
47285 ),
47286 'd' => array(
47287 'httpMethod' => 'DELETE',
47288 'extends' => array('b', 'c'),
47289 'parameters' => array(
47290 'test' => array()
47291 )
47292 )
47293 )
47294 ));
47295
47296 $command = $description->getOperation('d');
47297 $this->assertEquals('DELETE', $command->getHttpMethod());
47298 $this->assertContains('a1', $command->getParamNames());
47299 $this->assertContains('b2', $command->getParamNames());
47300 $this->assertContains('c3', $command->getParamNames());
47301 $this->assertContains('test', $command->getParamNames());
47302
47303 $this->assertTrue($command->getParam('a1')->getRequired());
47304 $this->assertEquals('bar', $command->getParam('a1')->getDefault());
47305 $this->assertEquals('test', $command->getParam('a1')->getDescription());
47306 }
47307
47308 public function testAddsOtherFields()
47309 {
47310 $description = ServiceDescription::factory(array(
47311 'operations' => array(),
47312 'description' => 'Foo',
47313 'apiVersion' => 'bar'
47314 ));
47315 $this->assertEquals('Foo', $description->getDescription());
47316 $this->assertEquals('bar', $description->getApiVersion());
47317 }
47318
47319 public function testCanLoadNestedExtends()
47320 {
47321 $description = ServiceDescription::factory(array(
47322 'operations' => array(
47323 'root' => array(
47324 'class' => 'foo'
47325 ),
47326 'foo' => array(
47327 'extends' => 'root',
47328 'parameters' => array(
47329 'baz' => array('type' => 'string')
47330 )
47331 ),
47332 'foo_2' => array(
47333 'extends' => 'foo',
47334 'parameters' => array(
47335 'bar' => array('type' => 'string')
47336 )
47337 ),
47338 'foo_3' => array(
47339 'class' => 'bar',
47340 'parameters' => array(
47341 'bar2' => array('type' => 'string')
47342 )
47343 ),
47344 'foo_4' => array(
47345 'extends' => array('foo_2', 'foo_3'),
47346 'parameters' => array(
47347 'bar3' => array('type' => 'string')
47348 )
47349 )
47350 )
47351 ));
47352
47353 $this->assertTrue($description->hasOperation('foo_4'));
47354 $foo4 = $description->getOperation('foo_4');
47355 $this->assertTrue($foo4->hasParam('baz'));
47356 $this->assertTrue($foo4->hasParam('bar'));
47357 $this->assertTrue($foo4->hasParam('bar2'));
47358 $this->assertTrue($foo4->hasParam('bar3'));
47359 $this->assertEquals('bar', $foo4->getClass());
47360 }
47361 }
47362 <?php
47363
47364 namespace Guzzle\Tests\Service\Description;
47365
47366 use Guzzle\Service\Description\Operation;
47367 use Guzzle\Service\Description\Parameter;
47368 use Guzzle\Service\Description\ServiceDescription;
47369
47370 /**
47371 * @covers Guzzle\Service\Description\Operation
47372 */
47373 class OperationTest extends \Guzzle\Tests\GuzzleTestCase
47374 {
47375 public static function strtoupper($string)
47376 {
47377 return strtoupper($string);
47378 }
47379
47380 public function testOperationIsDataObject()
47381 {
47382 $c = new Operation(array(
47383 'name' => 'test',
47384 'summary' => 'doc',
47385 'notes' => 'notes',
47386 'documentationUrl' => 'http://www.example.com',
47387 'httpMethod' => 'POST',
47388 'uri' => '/api/v1',
47389 'responseClass' => 'array',
47390 'responseNotes' => 'returns the json_decoded response',
47391 'deprecated' => true,
47392 'parameters' => array(
47393 'key' => array(
47394 'required' => true,
47395 'type' => 'string',
47396 'maxLength' => 10
47397 ),
47398 'key_2' => array(
47399 'required' => true,
47400 'type' => 'integer',
47401 'default' => 10
47402 )
47403 )
47404 ));
47405
47406 $this->assertEquals('test', $c->getName());
47407 $this->assertEquals('doc', $c->getSummary());
47408 $this->assertEquals('http://www.example.com', $c->getDocumentationUrl());
47409 $this->assertEquals('POST', $c->getHttpMethod());
47410 $this->assertEquals('/api/v1', $c->getUri());
47411 $this->assertEquals('array', $c->getResponseClass());
47412 $this->assertEquals('returns the json_decoded response', $c->getResponseNotes());
47413 $this->assertTrue($c->getDeprecated());
47414 $this->assertEquals('Guzzle\\Service\\Command\\OperationCommand', $c->getClass());
47415 $this->assertEquals(array(
47416 'key' => new Parameter(array(
47417 'name' => 'key',
47418 'required' => true,
47419 'type' => 'string',
47420 'maxLength' => 10,
47421 'parent' => $c
47422 )),
47423 'key_2' => new Parameter(array(
47424 'name' => 'key_2',
47425 'required' => true,
47426 'type' => 'integer',
47427 'default' => 10,
47428 'parent' => $c
47429 ))
47430 ), $c->getParams());
47431
47432 $this->assertEquals(new Parameter(array(
47433 'name' => 'key_2',
47434 'required' => true,
47435 'type' => 'integer',
47436 'default' => 10,
47437 'parent' => $c
47438 )), $c->getParam('key_2'));
47439
47440 $this->assertNull($c->getParam('afefwef'));
47441 $this->assertArrayNotHasKey('parent', $c->getParam('key_2')->toArray());
47442 }
47443
47444 public function testAllowsConcreteCommands()
47445 {
47446 $c = new Operation(array(
47447 'name' => 'test',
47448 'class' => 'Guzzle\\Service\\Command\ClosureCommand',
47449 'parameters' => array(
47450 'p' => new Parameter(array(
47451 'name' => 'foo'
47452 ))
47453 )
47454 ));
47455 $this->assertEquals('Guzzle\\Service\\Command\ClosureCommand', $c->getClass());
47456 }
47457
47458 public function testConvertsToArray()
47459 {
47460 $data = array(
47461 'name' => 'test',
47462 'class' => 'Guzzle\\Service\\Command\ClosureCommand',
47463 'summary' => 'test',
47464 'documentationUrl' => 'http://www.example.com',
47465 'httpMethod' => 'PUT',
47466 'uri' => '/',
47467 'parameters' => array('p' => array('name' => 'foo'))
47468 );
47469 $c = new Operation($data);
47470 $toArray = $c->toArray();
47471 unset($data['name']);
47472 $this->assertArrayHasKey('parameters', $toArray);
47473 $this->assertInternalType('array', $toArray['parameters']);
47474
47475 // Normalize the array
47476 unset($data['parameters']);
47477 unset($toArray['parameters']);
47478
47479 $data['responseType'] = 'primitive';
47480 $data['responseClass'] = 'array';
47481 $this->assertEquals($data, $toArray);
47482 }
47483
47484 public function testDeterminesIfHasParam()
47485 {
47486 $command = $this->getTestCommand();
47487 $this->assertTrue($command->hasParam('data'));
47488 $this->assertFalse($command->hasParam('baz'));
47489 }
47490
47491 public function testReturnsParamNames()
47492 {
47493 $command = $this->getTestCommand();
47494 $this->assertEquals(array('data'), $command->getParamNames());
47495 }
47496
47497 protected function getTestCommand()
47498 {
47499 return new Operation(array(
47500 'parameters' => array(
47501 'data' => new Parameter(array(
47502 'type' => 'string'
47503 ))
47504 )
47505 ));
47506 }
47507
47508 public function testCanBuildUpCommands()
47509 {
47510 $c = new Operation(array());
47511 $c->setName('foo')
47512 ->setClass('Baz')
47513 ->setDeprecated(false)
47514 ->setSummary('summary')
47515 ->setDocumentationUrl('http://www.foo.com')
47516 ->setHttpMethod('PUT')
47517 ->setResponseNotes('oh')
47518 ->setResponseClass('string')
47519 ->setUri('/foo/bar')
47520 ->addParam(new Parameter(array(
47521 'name' => 'test'
47522 )));
47523
47524 $this->assertEquals('foo', $c->getName());
47525 $this->assertEquals('Baz', $c->getClass());
47526 $this->assertEquals(false, $c->getDeprecated());
47527 $this->assertEquals('summary', $c->getSummary());
47528 $this->assertEquals('http://www.foo.com', $c->getDocumentationUrl());
47529 $this->assertEquals('PUT', $c->getHttpMethod());
47530 $this->assertEquals('oh', $c->getResponseNotes());
47531 $this->assertEquals('string', $c->getResponseClass());
47532 $this->assertEquals('/foo/bar', $c->getUri());
47533 $this->assertEquals(array('test'), $c->getParamNames());
47534 }
47535
47536 public function testCanRemoveParams()
47537 {
47538 $c = new Operation(array());
47539 $c->addParam(new Parameter(array('name' => 'foo')));
47540 $this->assertTrue($c->hasParam('foo'));
47541 $c->removeParam('foo');
47542 $this->assertFalse($c->hasParam('foo'));
47543 }
47544
47545 public function testAddsNameToParametersIfNeeded()
47546 {
47547 $command = new Operation(array('parameters' => array('foo' => new Parameter(array()))));
47548 $this->assertEquals('foo', $command->getParam('foo')->getName());
47549 }
47550
47551 public function testContainsApiErrorInformation()
47552 {
47553 $command = $this->getOperation();
47554 $this->assertEquals(1, count($command->getErrorResponses()));
47555 $arr = $command->toArray();
47556 $this->assertEquals(1, count($arr['errorResponses']));
47557 $command->addErrorResponse(400, 'Foo', 'Baz\\Bar');
47558 $this->assertEquals(2, count($command->getErrorResponses()));
47559 $command->setErrorResponses(array());
47560 $this->assertEquals(0, count($command->getErrorResponses()));
47561 }
47562
47563 public function testHasNotes()
47564 {
47565 $o = new Operation(array('notes' => 'foo'));
47566 $this->assertEquals('foo', $o->getNotes());
47567 $o->setNotes('bar');
47568 $this->assertEquals('bar', $o->getNotes());
47569 }
47570
47571 public function testHasData()
47572 {
47573 $o = new Operation(array('data' => array('foo' => 'baz', 'bar' => 123)));
47574 $o->setData('test', false);
47575 $this->assertEquals('baz', $o->getData('foo'));
47576 $this->assertEquals(123, $o->getData('bar'));
47577 $this->assertNull($o->getData('wfefwe'));
47578 $this->assertEquals(array(
47579 'parameters' => array(),
47580 'class' => 'Guzzle\Service\Command\OperationCommand',
47581 'data' => array('foo' => 'baz', 'bar' => 123, 'test' => false),
47582 'responseClass' => 'array',
47583 'responseType' => 'primitive'
47584 ), $o->toArray());
47585 }
47586
47587 public function testHasServiceDescription()
47588 {
47589 $s = new ServiceDescription();
47590 $o = new Operation(array(), $s);
47591 $this->assertSame($s, $o->getServiceDescription());
47592 }
47593
47594 /**
47595 * @expectedException Guzzle\Common\Exception\InvalidArgumentException
47596 */
47597 public function testValidatesResponseType()
47598 {
47599 $o = new Operation(array('responseClass' => 'array', 'responseType' => 'foo'));
47600 }
47601
47602 public function testInfersResponseType()
47603 {
47604 $o = $this->getOperation();
47605 $o->setServiceDescription(new ServiceDescription(array('models' => array('Foo' => array()))));
47606 $this->assertEquals('primitive', $o->getResponseType());
47607 $this->assertEquals('primitive', $o->setResponseClass('boolean')->getResponseType());
47608 $this->assertEquals('primitive', $o->setResponseClass('array')->getResponseType());
47609 $this->assertEquals('primitive', $o->setResponseClass('integer')->getResponseType());
47610 $this->assertEquals('primitive', $o->setResponseClass('string')->getResponseType());
47611 $this->assertEquals('class', $o->setResponseClass('foo')->getResponseType());
47612 $this->assertEquals('class', $o->setResponseClass(__CLASS__)->getResponseType());
47613 $this->assertEquals('model', $o->setResponseClass('Foo')->getResponseType());
47614 }
47615
47616 public function testHasResponseType()
47617 {
47618 // infers in the constructor
47619 $o = new Operation(array('responseClass' => 'array'));
47620 $this->assertEquals('primitive', $o->getResponseType());
47621 // Infers when set
47622 $o = new Operation();
47623 $this->assertEquals('primitive', $o->getResponseType());
47624 $this->assertEquals('model', $o->setResponseType('model')->getResponseType());
47625 }
47626
47627 public function testHasAdditionalParameters()
47628 {
47629 $o = new Operation(array(
47630 'additionalParameters' => array(
47631 'type' => 'string', 'name' => 'binks'
47632 ),
47633 'parameters' => array(
47634 'foo' => array('type' => 'integer')
47635 )
47636 ));
47637 $this->assertEquals('string', $o->getAdditionalParameters()->getType());
47638 $arr = $o->toArray();
47639 $this->assertEquals(array(
47640 'type' => 'string'
47641 ), $arr['additionalParameters']);
47642 }
47643
47644 /**
47645 * @return Operation
47646 */
47647 protected function getOperation()
47648 {
47649 return new Operation(array(
47650 'name' => 'OperationTest',
47651 'class' => get_class($this),
47652 'parameters' => array(
47653 'test' => array('type' => 'object'),
47654 'bool_1' => array('default' => true, 'type' => 'boolean'),
47655 'bool_2' => array('default' => false),
47656 'float' => array('type' => 'numeric'),
47657 'int' => array('type' => 'integer'),
47658 'date' => array('type' => 'string'),
47659 'timestamp' => array('type' => 'string'),
47660 'string' => array('type' => 'string'),
47661 'username' => array('type' => 'string', 'required' => true, 'filters' => 'strtolower'),
47662 'test_function' => array('type' => 'string', 'filters' => __CLASS__ . '::strtoupper')
47663 ),
47664 'errorResponses' => array(
47665 array('code' => 503, 'reason' => 'InsufficientCapacity', 'class' => 'Guzzle\\Exception\\RuntimeException')
47666 )
47667 ));
47668 }
47669 }
47670 <?php
47671
47672 namespace Guzzle\Tests\Service\Description;
47673
47674 use Guzzle\Service\Description\ServiceDescription;
47675 use Guzzle\Service\Description\Operation;
47676 use Guzzle\Service\Description\Parameter;
47677 use Guzzle\Service\Client;
47678
47679 /**
47680 * @covers Guzzle\Service\Description\ServiceDescription
47681 */
47682 class ServiceDescriptionTest extends \Guzzle\Tests\GuzzleTestCase
47683 {
47684 protected $serviceData;
47685
47686 public function setup()
47687 {
47688 $this->serviceData = array(
47689 'test_command' => new Operation(array(
47690 'name' => 'test_command',
47691 'description' => 'documentationForCommand',
47692 'httpMethod' => 'DELETE',
47693 'class' => 'Guzzle\\Tests\\Service\\Mock\\Command\\MockCommand',
47694 'parameters' => array(
47695 'bucket' => array('required' => true),
47696 'key' => array('required' => true)
47697 )
47698 ))
47699 );
47700 }
47701
47702 /**
47703 * @covers Guzzle\Service\Description\ServiceDescription::factory
47704 * @covers Guzzle\Service\Description\ServiceDescriptionLoader::build
47705 */
47706 public function testFactoryDelegatesToConcreteFactories()
47707 {
47708 $jsonFile = __DIR__ . '/../../TestData/test_service.json';
47709 $this->assertInstanceOf('Guzzle\Service\Description\ServiceDescription', ServiceDescription::factory($jsonFile));
47710 }
47711
47712 public function testConstructor()
47713 {
47714 $service = new ServiceDescription(array('operations' => $this->serviceData));
47715 $this->assertEquals(1, count($service->getOperations()));
47716 $this->assertFalse($service->hasOperation('foobar'));
47717 $this->assertTrue($service->hasOperation('test_command'));
47718 }
47719
47720 public function testIsSerializable()
47721 {
47722 $service = new ServiceDescription(array('operations' => $this->serviceData));
47723 $data = serialize($service);
47724 $d2 = unserialize($data);
47725 $this->assertEquals(serialize($service), serialize($d2));
47726 }
47727
47728 public function testSerializesParameters()
47729 {
47730 $service = new ServiceDescription(array(
47731 'operations' => array(
47732 'foo' => new Operation(array('parameters' => array('foo' => array('type' => 'string'))))
47733 )
47734 ));
47735 $serialized = serialize($service);
47736 $this->assertContains('"parameters":{"foo":', $serialized);
47737 $service = unserialize($serialized);
47738 $this->assertTrue($service->getOperation('foo')->hasParam('foo'));
47739 }
47740
47741 public function testAllowsForJsonBasedArrayParamsFunctionalTest()
47742 {
47743 $description = new ServiceDescription(array(
47744 'operations' => array(
47745 'test' => new Operation(array(
47746 'httpMethod' => 'PUT',
47747 'parameters' => array(
47748 'data' => array(
47749 'required' => true,
47750 'filters' => 'json_encode',
47751 'location' => 'body'
47752 )
47753 )
47754 ))
47755 )
47756 ));
47757 $client = new Client();
47758 $client->setDescription($description);
47759 $command = $client->getCommand('test', array(
47760 'data' => array(
47761 'foo' => 'bar'
47762 )
47763 ));
47764
47765 $request = $command->prepare();
47766 $this->assertEquals('{"foo":"bar"}', (string) $request->getBody());
47767 }
47768
47769 public function testContainsModels()
47770 {
47771 $d = new ServiceDescription(array(
47772 'operations' => array('foo' => array()),
47773 'models' => array(
47774 'Tag' => array('type' => 'object'),
47775 'Person' => array('type' => 'object')
47776 )
47777 ));
47778 $this->assertTrue($d->hasModel('Tag'));
47779 $this->assertTrue($d->hasModel('Person'));
47780 $this->assertFalse($d->hasModel('Foo'));
47781 $this->assertInstanceOf('Guzzle\Service\Description\Parameter', $d->getModel('Tag'));
47782 $this->assertNull($d->getModel('Foo'));
47783 $this->assertContains('"models":{', serialize($d));
47784 $this->assertEquals(array('Tag', 'Person'), array_keys($d->getModels()));
47785 }
47786
47787 public function testCanAddModels()
47788 {
47789 $d = new ServiceDescription(array());
47790 $this->assertFalse($d->hasModel('Foo'));
47791 $d->addModel(new Parameter(array('name' => 'Foo')));
47792 $this->assertTrue($d->hasModel('Foo'));
47793 }
47794
47795 public function testHasAttributes()
47796 {
47797 $d = new ServiceDescription(array(
47798 'operations' => array(),
47799 'name' => 'Name',
47800 'description' => 'Description',
47801 'apiVersion' => '1.24'
47802 ));
47803
47804 $this->assertEquals('Name', $d->getName());
47805 $this->assertEquals('Description', $d->getDescription());
47806 $this->assertEquals('1.24', $d->getApiVersion());
47807
47808 $s = serialize($d);
47809 $this->assertContains('"name":"Name"', $s);
47810 $this->assertContains('"description":"Description"', $s);
47811 $this->assertContains('"apiVersion":"1.24"', $s);
47812
47813 $d = unserialize($s);
47814 $this->assertEquals('Name', $d->getName());
47815 $this->assertEquals('Description', $d->getDescription());
47816 $this->assertEquals('1.24', $d->getApiVersion());
47817 }
47818
47819 public function testPersistsCustomAttributes()
47820 {
47821 $data = array(
47822 'operations' => array('foo' => array('class' => 'foo', 'parameters' => array())),
47823 'name' => 'Name',
47824 'description' => 'Test',
47825 'apiVersion' => '1.24',
47826 'auth' => 'foo',
47827 'keyParam' => 'bar'
47828 );
47829 $d = new ServiceDescription($data);
47830 $d->setData('hello', 'baz');
47831 $this->assertEquals('foo', $d->getData('auth'));
47832 $this->assertEquals('baz', $d->getData('hello'));
47833 $this->assertEquals('bar', $d->getData('keyParam'));
47834 // responseClass and responseType are added by default
47835 $data['operations']['foo']['responseClass'] = 'array';
47836 $data['operations']['foo']['responseType'] = 'primitive';
47837 $this->assertEquals($data + array('hello' => 'baz'), json_decode($d->serialize(), true));
47838 }
47839
47840 public function testHasToArray()
47841 {
47842 $data = array(
47843 'operations' => array(),
47844 'name' => 'Name',
47845 'description' => 'Test'
47846 );
47847 $d = new ServiceDescription($data);
47848 $arr = $d->toArray();
47849 $this->assertEquals('Name', $arr['name']);
47850 $this->assertEquals('Test', $arr['description']);
47851 }
47852
47853 public function testReturnsNullWhenRetrievingMissingOperation()
47854 {
47855 $s = new ServiceDescription(array());
47856 $this->assertNull($s->getOperation('foo'));
47857 }
47858
47859 public function testCanAddOperations()
47860 {
47861 $s = new ServiceDescription(array());
47862 $this->assertFalse($s->hasOperation('Foo'));
47863 $s->addOperation(new Operation(array('name' => 'Foo')));
47864 $this->assertTrue($s->hasOperation('Foo'));
47865 }
47866
47867 /**
47868 * @expectedException Guzzle\Common\Exception\InvalidArgumentException
47869 */
47870 public function testValidatesOperationTypes()
47871 {
47872 $s = new ServiceDescription(array(
47873 'operations' => array('foo' => new \stdClass())
47874 ));
47875 }
47876
47877 public function testHasBaseUrl()
47878 {
47879 $description = new ServiceDescription(array('baseUrl' => 'http://foo.com'));
47880 $this->assertEquals('http://foo.com', $description->getBaseUrl());
47881 $description->setBaseUrl('http://foobar.com');
47882 $this->assertEquals('http://foobar.com', $description->getBaseUrl());
47883 }
47884
47885 public function testCanUseBasePath()
47886 {
47887 $description = new ServiceDescription(array('basePath' => 'http://foo.com'));
47888 $this->assertEquals('http://foo.com', $description->getBaseUrl());
47889 }
47890
47891 public function testModelsHaveNames()
47892 {
47893 $desc = array(
47894 'models' => array(
47895 'date' => array('type' => 'string'),
47896 'user'=> array(
47897 'type' => 'object',
47898 'properties' => array(
47899 'dob' => array('$ref' => 'date')
47900 )
47901 )
47902 )
47903 );
47904
47905 $s = ServiceDescription::factory($desc);
47906 $this->assertEquals('date', $s->getModel('date')->getName());
47907 $this->assertEquals('dob', $s->getModel('user')->getProperty('dob')->getName());
47908 }
47909 }
47910 <?php
47911
47912 namespace Guzzle\Tests\Service\Description;
47913
47914 use Guzzle\Service\Description\Parameter;
47915 use Guzzle\Service\Description\SchemaValidator;
47916
47917 /**
47918 * @covers Guzzle\Service\Description\SchemaValidator
47919 */
47920 class SchemaValidatorTest extends \Guzzle\Tests\GuzzleTestCase
47921 {
47922 /** @var SchemaValidator */
47923 protected $validator;
47924
47925 public function setUp()
47926 {
47927 $this->validator = new SchemaValidator();
47928 }
47929
47930 public function testValidatesArrayListsAreNumericallyIndexed()
47931 {
47932 $value = array(array(1));
47933 $this->assertFalse($this->validator->validate($this->getComplexParam(), $value));
47934 $this->assertEquals(
47935 array('[Foo][0] must be an array of properties. Got a numerically indexed array.'),
47936 $this->validator->getErrors()
47937 );
47938 }
47939
47940 public function testValidatesArrayListsContainProperItems()
47941 {
47942 $value = array(true);
47943 $this->assertFalse($this->validator->validate($this->getComplexParam(), $value));
47944 $this->assertEquals(
47945 array('[Foo][0] must be of type object'),
47946 $this->validator->getErrors()
47947 );
47948 }
47949
47950 public function testAddsDefaultValuesInLists()
47951 {
47952 $value = array(array());
47953 $this->assertTrue($this->validator->validate($this->getComplexParam(), $value));
47954 $this->assertEquals(array(array('Bar' => true)), $value);
47955 }
47956
47957 public function testMergesDefaultValuesInLists()
47958 {
47959 $value = array(
47960 array('Baz' => 'hello!'),
47961 array('Bar' => false)
47962 );
47963 $this->assertTrue($this->validator->validate($this->getComplexParam(), $value));
47964 $this->assertEquals(array(
47965 array(
47966 'Baz' => 'hello!',
47967 'Bar' => true
47968 ),
47969 array('Bar' => false)
47970 ), $value);
47971 }
47972
47973 public function testCorrectlyConvertsParametersToArrayWhenArraysArePresent()
47974 {
47975 $param = $this->getComplexParam();
47976 $result = $param->toArray();
47977 $this->assertInternalType('array', $result['items']);
47978 $this->assertEquals('array', $result['type']);
47979 $this->assertInstanceOf('Guzzle\Service\Description\Parameter', $param->getItems());
47980 }
47981
47982 public function testAllowsInstanceOf()
47983 {
47984 $p = new Parameter(array(
47985 'name' => 'foo',
47986 'type' => 'object',
47987 'instanceOf' => get_class($this)
47988 ));
47989 $this->assertTrue($this->validator->validate($p, $this));
47990 $this->assertFalse($this->validator->validate($p, $p));
47991 $this->assertEquals(array('[foo] must be an instance of ' . __CLASS__), $this->validator->getErrors());
47992 }
47993
47994 public function testEnforcesInstanceOfOnlyWhenObject()
47995 {
47996 $p = new Parameter(array(
47997 'name' => 'foo',
47998 'type' => array('object', 'string'),
47999 'instanceOf' => get_class($this)
48000 ));
48001 $this->assertTrue($this->validator->validate($p, $this));
48002 $s = 'test';
48003 $this->assertTrue($this->validator->validate($p, $s));
48004 }
48005
48006 public function testConvertsObjectsToArraysWhenToArrayInterface()
48007 {
48008 $o = $this->getMockBuilder('Guzzle\Common\ToArrayInterface')
48009 ->setMethods(array('toArray'))
48010 ->getMockForAbstractClass();
48011 $o->expects($this->once())
48012 ->method('toArray')
48013 ->will($this->returnValue(array(
48014 'foo' => 'bar'
48015 )));
48016 $p = new Parameter(array(
48017 'name' => 'test',
48018 'type' => 'object',
48019 'properties' => array(
48020 'foo' => array('required' => 'true')
48021 )
48022 ));
48023 $this->assertTrue($this->validator->validate($p, $o));
48024 }
48025
48026 public function testMergesValidationErrorsInPropertiesWithParent()
48027 {
48028 $p = new Parameter(array(
48029 'name' => 'foo',
48030 'type' => 'object',
48031 'properties' => array(
48032 'bar' => array('type' => 'string', 'required' => true, 'description' => 'This is what it does'),
48033 'test' => array('type' => 'string', 'minLength' => 2, 'maxLength' => 5),
48034 'test2' => array('type' => 'string', 'minLength' => 2, 'maxLength' => 2),
48035 'test3' => array('type' => 'integer', 'minimum' => 100),
48036 'test4' => array('type' => 'integer', 'maximum' => 10),
48037 'test5' => array('type' => 'array', 'maxItems' => 2),
48038 'test6' => array('type' => 'string', 'enum' => array('a', 'bc')),
48039 'test7' => array('type' => 'string', 'pattern' => '/[0-9]+/'),
48040 'test8' => array('type' => 'number'),
48041 'baz' => array(
48042 'type' => 'array',
48043 'minItems' => 2,
48044 'required' => true,
48045 "items" => array("type" => "string")
48046 )
48047 )
48048 ));
48049
48050 $value = array(
48051 'test' => 'a',
48052 'test2' => 'abc',
48053 'baz' => array(false),
48054 'test3' => 10,
48055 'test4' => 100,
48056 'test5' => array(1, 3, 4),
48057 'test6' => 'Foo',
48058 'test7' => 'abc',
48059 'test8' => 'abc'
48060 );
48061
48062 $this->assertFalse($this->validator->validate($p, $value));
48063 $this->assertEquals(array (
48064 '[foo][bar] is a required string: This is what it does',
48065 '[foo][baz] must contain 2 or more elements',
48066 '[foo][baz][0] must be of type string',
48067 '[foo][test2] length must be less than or equal to 2',
48068 '[foo][test3] must be greater than or equal to 100',
48069 '[foo][test4] must be less than or equal to 10',
48070 '[foo][test5] must contain 2 or fewer elements',
48071 '[foo][test6] must be one of "a" or "bc"',
48072 '[foo][test7] must match the following regular expression: /[0-9]+/',
48073 '[foo][test8] must be of type number',
48074 '[foo][test] length must be greater than or equal to 2',
48075 ), $this->validator->getErrors());
48076 }
48077
48078 public function testHandlesNullValuesInArraysWithDefaults()
48079 {
48080 $p = new Parameter(array(
48081 'name' => 'foo',
48082 'type' => 'object',
48083 'required' => true,
48084 'properties' => array(
48085 'bar' => array(
48086 'type' => 'object',
48087 'required' => true,
48088 'properties' => array(
48089 'foo' => array('default' => 'hi')
48090 )
48091 )
48092 )
48093 ));
48094 $value = array();
48095 $this->assertTrue($this->validator->validate($p, $value));
48096 $this->assertEquals(array('bar' => array('foo' => 'hi')), $value);
48097 }
48098
48099 public function testFailsWhenNullValuesInArraysWithNoDefaults()
48100 {
48101 $p = new Parameter(array(
48102 'name' => 'foo',
48103 'type' => 'object',
48104 'required' => true,
48105 'properties' => array(
48106 'bar' => array(
48107 'type' => 'object',
48108 'required' => true,
48109 'properties' => array('foo' => array('type' => 'string'))
48110 )
48111 )
48112 ));
48113 $value = array();
48114 $this->assertFalse($this->validator->validate($p, $value));
48115 $this->assertEquals(array('[foo][bar] is a required object'), $this->validator->getErrors());
48116 }
48117
48118 public function testChecksTypes()
48119 {
48120 $p = new SchemaValidator();
48121 $r = new \ReflectionMethod($p, 'determineType');
48122 $r->setAccessible(true);
48123 $this->assertEquals('any', $r->invoke($p, 'any', 'hello'));
48124 $this->assertEquals(false, $r->invoke($p, 'foo', 'foo'));
48125 $this->assertEquals('string', $r->invoke($p, 'string', 'hello'));
48126 $this->assertEquals(false, $r->invoke($p, 'string', false));
48127 $this->assertEquals('integer', $r->invoke($p, 'integer', 1));
48128 $this->assertEquals(false, $r->invoke($p, 'integer', 'abc'));
48129 $this->assertEquals('numeric', $r->invoke($p, 'numeric', 1));
48130 $this->assertEquals('numeric', $r->invoke($p, 'numeric', '1'));
48131 $this->assertEquals('number', $r->invoke($p, 'number', 1));
48132 $this->assertEquals('number', $r->invoke($p, 'number', '1'));
48133 $this->assertEquals(false, $r->invoke($p, 'numeric', 'a'));
48134 $this->assertEquals('boolean', $r->invoke($p, 'boolean', true));
48135 $this->assertEquals('boolean', $r->invoke($p, 'boolean', false));
48136 $this->assertEquals(false, $r->invoke($p, 'boolean', 'false'));
48137 $this->assertEquals('null', $r->invoke($p, 'null', null));
48138 $this->assertEquals(false, $r->invoke($p, 'null', 'abc'));
48139 $this->assertEquals('array', $r->invoke($p, 'array', array()));
48140 $this->assertEquals(false, $r->invoke($p, 'array', 'foo'));
48141 }
48142
48143 public function testValidatesFalseAdditionalProperties()
48144 {
48145 $param = new Parameter(array(
48146 'name' => 'foo',
48147 'type' => 'object',
48148 'properties' => array('bar' => array('type' => 'string')),
48149 'additionalProperties' => false
48150 ));
48151 $value = array('test' => '123');
48152 $this->assertFalse($this->validator->validate($param, $value));
48153 $this->assertEquals(array('[foo][test] is not an allowed property'), $this->validator->getErrors());
48154 $value = array('bar' => '123');
48155 $this->assertTrue($this->validator->validate($param, $value));
48156 }
48157
48158 public function testAllowsUndefinedAdditionalProperties()
48159 {
48160 $param = new Parameter(array(
48161 'name' => 'foo',
48162 'type' => 'object',
48163 'properties' => array('bar' => array('type' => 'string'))
48164 ));
48165 $value = array('test' => '123');
48166 $this->assertTrue($this->validator->validate($param, $value));
48167 }
48168
48169 public function testValidatesAdditionalProperties()
48170 {
48171 $param = new Parameter(array(
48172 'name' => 'foo',
48173 'type' => 'object',
48174 'properties' => array('bar' => array('type' => 'string')),
48175 'additionalProperties' => array('type' => 'integer')
48176 ));
48177 $value = array('test' => 'foo');
48178 $this->assertFalse($this->validator->validate($param, $value));
48179 $this->assertEquals(array('[foo][test] must be of type integer'), $this->validator->getErrors());
48180 }
48181
48182 public function testValidatesAdditionalPropertiesThatArrayArrays()
48183 {
48184 $param = new Parameter(array(
48185 'name' => 'foo',
48186 'type' => 'object',
48187 'additionalProperties' => array(
48188 'type' => 'array',
48189 'items' => array('type' => 'string')
48190 )
48191 ));
48192 $value = array('test' => array(true));
48193 $this->assertFalse($this->validator->validate($param, $value));
48194 $this->assertEquals(array('[foo][test][0] must be of type string'), $this->validator->getErrors());
48195 }
48196
48197 public function testIntegersCastToStringWhenTypeMismatch()
48198 {
48199 $param = new Parameter(array('name' => 'test', 'type' => 'string'));
48200 $value = 12;
48201 $this->assertTrue($this->validator->validate($param, $value));
48202 $this->assertEquals('12', $value);
48203 }
48204
48205 public function testRequiredMessageIncludesType()
48206 {
48207 $param = new Parameter(array('name' => 'test', 'type' => array('string', 'boolean'), 'required' => true));
48208 $value = null;
48209 $this->assertFalse($this->validator->validate($param, $value));
48210 $this->assertEquals(array('[test] is a required string or boolean'), $this->validator->getErrors());
48211 }
48212
48213 protected function getComplexParam()
48214 {
48215 return new Parameter(array(
48216 'name' => 'Foo',
48217 'type' => 'array',
48218 'required' => true,
48219 'min' => 1,
48220 'items' => array(
48221 'type' => 'object',
48222 'properties' => array(
48223 'Baz' => array(
48224 'type' => 'string',
48225 ),
48226 'Bar' => array(
48227 'required' => true,
48228 'type' => 'boolean',
48229 'default' => true
48230 )
48231 )
48232 )
48233 ));
48234 }
48235 }
48236 <?php
48237
48238 namespace Guzzle\Tests\Service;
48239
48240 /**
48241 * @covers Guzzle\Service\AbstractConfigLoader
48242 */
48243 class AbstractConfigLoaderTest extends \Guzzle\Tests\GuzzleTestCase
48244 {
48245 /** @var \Guzzle\Service\AbstractConfigLoader */
48246 protected $loader;
48247
48248 /** @var array Any files that need to be deleted on tear down */
48249 protected $cleanup = array();
48250
48251 public function setUp()
48252 {
48253 $this->loader = $this->getMockBuilder('Guzzle\Service\AbstractConfigLoader')
48254 ->setMethods(array('build'))
48255 ->getMockForAbstractClass();
48256 }
48257
48258 public function tearDown()
48259 {
48260 foreach ($this->cleanup as $file) {
48261 unlink($file);
48262 }
48263 }
48264
48265 /**
48266 * @expectedException \Guzzle\Common\Exception\InvalidArgumentException
48267 */
48268 public function testOnlyLoadsSupportedTypes()
48269 {
48270 $this->loader->load(new \stdClass());
48271 }
48272
48273 /**
48274 * @expectedException \Guzzle\Common\Exception\InvalidArgumentException
48275 * @expectedExceptionMessage Unable to open fooooooo.json
48276 */
48277 public function testFileMustBeReadable()
48278 {
48279 $this->loader->load('fooooooo.json');
48280 }
48281
48282 /**
48283 * @expectedException \Guzzle\Common\Exception\InvalidArgumentException
48284 * @expectedExceptionMessage Unknown file extension
48285 */
48286 public function testMustBeSupportedExtension()
48287 {
48288 $this->loader->load(dirname(__DIR__) . '/TestData/FileBody.txt');
48289 }
48290
48291 /**
48292 * @expectedException \Guzzle\Common\Exception\RuntimeException
48293 * @expectedExceptionMessage Error loading JSON data from
48294 */
48295 public function testJsonMustBeValue()
48296 {
48297 $filename = tempnam(sys_get_temp_dir(), 'json') . '.json';
48298 file_put_contents($filename, '{/{./{}foo');
48299 $this->cleanup[] = $filename;
48300 $this->loader->load($filename);
48301 }
48302
48303 /**
48304 * @expectedException \Guzzle\Common\Exception\InvalidArgumentException
48305 * @expectedExceptionMessage PHP files must return an array
48306 */
48307 public function testPhpFilesMustReturnAnArray()
48308 {
48309 $filename = tempnam(sys_get_temp_dir(), 'php') . '.php';
48310 file_put_contents($filename, '<?php $fdr = false;');
48311 $this->cleanup[] = $filename;
48312 $this->loader->load($filename);
48313 }
48314
48315 public function testLoadsPhpFileIncludes()
48316 {
48317 $filename = tempnam(sys_get_temp_dir(), 'php') . '.php';
48318 file_put_contents($filename, '<?php return array("foo" => "bar");');
48319 $this->cleanup[] = $filename;
48320 $this->loader->expects($this->exactly(1))->method('build')->will($this->returnArgument(0));
48321 $config = $this->loader->load($filename);
48322 $this->assertEquals(array('foo' => 'bar'), $config);
48323 }
48324
48325 public function testCanCreateFromJson()
48326 {
48327 $file = dirname(__DIR__) . '/TestData/services/json1.json';
48328 // The build method will just return the config data
48329 $this->loader->expects($this->exactly(1))->method('build')->will($this->returnArgument(0));
48330 $data = $this->loader->load($file);
48331 // Ensure that the config files were merged using the includes directives
48332 $this->assertArrayHasKey('includes', $data);
48333 $this->assertArrayHasKey('services', $data);
48334 $this->assertInternalType('array', $data['services']['foo']);
48335 $this->assertInternalType('array', $data['services']['abstract']);
48336 $this->assertInternalType('array', $data['services']['mock']);
48337 $this->assertEquals('bar', $data['services']['foo']['params']['baz']);
48338 }
48339
48340 public function testUsesAliases()
48341 {
48342 $file = dirname(__DIR__) . '/TestData/services/json1.json';
48343 $this->loader->addAlias('foo', $file);
48344 // The build method will just return the config data
48345 $this->loader->expects($this->exactly(1))->method('build')->will($this->returnArgument(0));
48346 $data = $this->loader->load('foo');
48347 $this->assertEquals('bar', $data['services']['foo']['params']['baz']);
48348 }
48349
48350 /**
48351 * @expectedException \Guzzle\Common\Exception\InvalidArgumentException
48352 * @expectedExceptionMessage Unable to open foo.json
48353 */
48354 public function testCanRemoveAliases()
48355 {
48356 $file = dirname(__DIR__) . '/TestData/services/json1.json';
48357 $this->loader->addAlias('foo.json', $file);
48358 $this->loader->removeAlias('foo.json');
48359 $this->loader->load('foo.json');
48360 }
48361
48362 public function testCanLoadArraysWithIncludes()
48363 {
48364 $file = dirname(__DIR__) . '/TestData/services/json1.json';
48365 $config = array('includes' => array($file));
48366 // The build method will just return the config data
48367 $this->loader->expects($this->exactly(1))->method('build')->will($this->returnArgument(0));
48368 $data = $this->loader->load($config);
48369 $this->assertEquals('bar', $data['services']['foo']['params']['baz']);
48370 }
48371
48372 public function testDoesNotEnterInfiniteLoop()
48373 {
48374 $prefix = $file = dirname(__DIR__) . '/TestData/description';
48375 $this->loader->load("{$prefix}/baz.json");
48376 $this->assertCount(4, $this->readAttribute($this->loader, 'loadedFiles'));
48377 // Ensure that the internal list of loaded files is reset
48378 $this->loader->load("{$prefix}/../test_service2.json");
48379 $this->assertCount(1, $this->readAttribute($this->loader, 'loadedFiles'));
48380 // Ensure that previously loaded files will be reloaded when starting fresh
48381 $this->loader->load("{$prefix}/baz.json");
48382 $this->assertCount(4, $this->readAttribute($this->loader, 'loadedFiles'));
48383 }
48384 }
48385 <?php
48386
48387 namespace Guzzle\Tests\Service\Resource;
48388
48389 use Guzzle\Service\Resource\Model;
48390 use Guzzle\Service\Description\Parameter;
48391 use Guzzle\Common\Collection;
48392
48393 /**
48394 * @covers Guzzle\Service\Resource\Model
48395 */
48396 class ModelTest extends \Guzzle\Tests\GuzzleTestCase
48397 {
48398 public function testOwnsStructure()
48399 {
48400 $param = new Parameter(array('type' => 'object'));
48401 $model = new Model(array('foo' => 'bar'), $param);
48402 $this->assertSame($param, $model->getStructure());
48403 $this->assertEquals('bar', $model->get('foo'));
48404 $this->assertEquals('bar', $model['foo']);
48405 }
48406
48407 public function testCanBeUsedWithoutStructure()
48408 {
48409 $model = new Model(array(
48410 'Foo' => 'baz',
48411 'Bar' => array(
48412 'Boo' => 'Bam'
48413 )
48414 ));
48415 $transform = function ($key, $value) {
48416 return ($value && is_array($value)) ? new Collection($value) : $value;
48417 };
48418 $model = $model->map($transform);
48419 $this->assertInstanceOf('Guzzle\Common\Collection', $model->getPath('Bar'));
48420 }
48421
48422 public function testAllowsFiltering()
48423 {
48424 $model = new Model(array(
48425 'Foo' => 'baz',
48426 'Bar' => 'a'
48427 ));
48428 $model = $model->filter(function ($i, $v) {
48429 return $v[0] == 'a';
48430 });
48431 $this->assertEquals(array('Bar' => 'a'), $model->toArray());
48432 }
48433
48434 public function testDoesNotIncludeEmptyStructureInString()
48435 {
48436 $model = new Model(array('Foo' => 'baz'));
48437 $str = (string) $model;
48438 $this->assertContains('Debug output of model', $str);
48439 $this->assertNotContains('Model structure', $str);
48440 }
48441
48442 public function testDoesIncludeModelStructureInString()
48443 {
48444 $model = new Model(array('Foo' => 'baz'), new Parameter(array('name' => 'Foo')));
48445 $str = (string) $model;
48446 $this->assertContains('Debug output of Foo model', $str);
48447 $this->assertContains('Model structure', $str);
48448 }
48449 }
48450 <?php
48451
48452 namespace Guzzle\Tests\Service\Resource;
48453
48454 use Guzzle\Service\Resource\ResourceIterator;
48455 use Guzzle\Tests\Service\Mock\Model\MockCommandIterator;
48456
48457 /**
48458 * @group server
48459 * @covers Guzzle\Service\Resource\ResourceIterator
48460 */
48461 class ResourceIteratorTest extends \Guzzle\Tests\GuzzleTestCase
48462 {
48463 public function testDescribesEvents()
48464 {
48465 $this->assertInternalType('array', ResourceIterator::getAllEvents());
48466 }
48467
48468 public function testConstructorConfiguresDefaults()
48469 {
48470 $ri = $this->getMockForAbstractClass('Guzzle\\Service\\Resource\\ResourceIterator', array(
48471 $this->getServiceBuilder()->get('mock')->getCommand('iterable_command'),
48472 array(
48473 'limit' => 10,
48474 'page_size' => 3
48475 )
48476 ), 'MockIterator');
48477
48478 $this->assertEquals(false, $ri->getNextToken());
48479 $this->assertEquals(false, $ri->current());
48480 }
48481
48482 public function testSendsRequestsForNextSetOfResources()
48483 {
48484 // Queue up an array of responses for iterating
48485 $this->getServer()->flush();
48486 $this->getServer()->enqueue(array(
48487 "HTTP/1.1 200 OK\r\nContent-Length: 52\r\n\r\n{ \"next_token\": \"g\", \"resources\": [\"d\", \"e\", \"f\"] }",
48488 "HTTP/1.1 200 OK\r\nContent-Length: 52\r\n\r\n{ \"next_token\": \"j\", \"resources\": [\"g\", \"h\", \"i\"] }",
48489 "HTTP/1.1 200 OK\r\nContent-Length: 41\r\n\r\n{ \"next_token\": \"\", \"resources\": [\"j\"] }"
48490 ));
48491
48492 // Create a new resource iterator using the IterableCommand mock
48493 $ri = new MockCommandIterator($this->getServiceBuilder()->get('mock')->getCommand('iterable_command'), array(
48494 'page_size' => 3
48495 ));
48496
48497 // Ensure that no requests have been sent yet
48498 $this->assertEquals(0, count($this->getServer()->getReceivedRequests(false)));
48499
48500 //$this->assertEquals(array('d', 'e', 'f', 'g', 'h', 'i', 'j'), $ri->toArray());
48501 $ri->toArray();
48502 $requests = $this->getServer()->getReceivedRequests(true);
48503 $this->assertEquals(3, count($requests));
48504
48505 $this->assertEquals(3, $requests[0]->getQuery()->get('page_size'));
48506 $this->assertEquals(3, $requests[1]->getQuery()->get('page_size'));
48507 $this->assertEquals(3, $requests[2]->getQuery()->get('page_size'));
48508
48509 // Reset and resend
48510 $this->getServer()->flush();
48511 $this->getServer()->enqueue(array(
48512 "HTTP/1.1 200 OK\r\nContent-Length: 52\r\n\r\n{ \"next_token\": \"g\", \"resources\": [\"d\", \"e\", \"f\"] }",
48513 "HTTP/1.1 200 OK\r\nContent-Length: 52\r\n\r\n{ \"next_token\": \"j\", \"resources\": [\"g\", \"h\", \"i\"] }",
48514 "HTTP/1.1 200 OK\r\nContent-Length: 41\r\n\r\n{ \"next_token\": \"\", \"resources\": [\"j\"] }",
48515 ));
48516
48517 $d = array();
48518 reset($ri);
48519 foreach ($ri as $data) {
48520 $d[] = $data;
48521 }
48522 $this->assertEquals(array('d', 'e', 'f', 'g', 'h', 'i', 'j'), $d);
48523 }
48524
48525 public function testCalculatesPageSize()
48526 {
48527 $this->getServer()->flush();
48528 $this->getServer()->enqueue(array(
48529 "HTTP/1.1 200 OK\r\nContent-Length: 52\r\n\r\n{ \"next_token\": \"g\", \"resources\": [\"d\", \"e\", \"f\"] }",
48530 "HTTP/1.1 200 OK\r\nContent-Length: 52\r\n\r\n{ \"next_token\": \"j\", \"resources\": [\"g\", \"h\", \"i\"] }",
48531 "HTTP/1.1 200 OK\r\nContent-Length: 52\r\n\r\n{ \"next_token\": \"j\", \"resources\": [\"j\", \"k\"] }"
48532 ));
48533
48534 $ri = new MockCommandIterator($this->getServiceBuilder()->get('mock')->getCommand('iterable_command'), array(
48535 'page_size' => 3,
48536 'limit' => 7
48537 ));
48538
48539 $this->assertEquals(array('d', 'e', 'f', 'g', 'h', 'i', 'j'), $ri->toArray());
48540 $requests = $this->getServer()->getReceivedRequests(true);
48541 $this->assertEquals(3, count($requests));
48542 $this->assertEquals(3, $requests[0]->getQuery()->get('page_size'));
48543 $this->assertEquals(3, $requests[1]->getQuery()->get('page_size'));
48544 $this->assertEquals(1, $requests[2]->getQuery()->get('page_size'));
48545 }
48546
48547 public function testUseAsArray()
48548 {
48549 $this->getServer()->flush();
48550 $this->getServer()->enqueue(array(
48551 "HTTP/1.1 200 OK\r\nContent-Length: 52\r\n\r\n{ \"next_token\": \"g\", \"resources\": [\"d\", \"e\", \"f\"] }",
48552 "HTTP/1.1 200 OK\r\nContent-Length: 52\r\n\r\n{ \"next_token\": \"\", \"resources\": [\"g\", \"h\", \"i\"] }"
48553 ));
48554
48555 $ri = new MockCommandIterator($this->getServiceBuilder()->get('mock')->getCommand('iterable_command'));
48556
48557 // Ensure that the key is never < 0
48558 $this->assertEquals(0, $ri->key());
48559 $this->assertEquals(0, count($ri));
48560
48561 // Ensure that the iterator can be used as KVP array
48562 $data = array();
48563 foreach ($ri as $key => $value) {
48564 $data[$key] = $value;
48565 }
48566
48567 // Ensure that the iterate is countable
48568 $this->assertEquals(6, count($ri));
48569 $this->assertEquals(array('d', 'e', 'f', 'g', 'h', 'i'), $data);
48570 }
48571
48572 public function testBailsWhenSendReturnsNoResults()
48573 {
48574 $this->getServer()->flush();
48575 $this->getServer()->enqueue(array(
48576 "HTTP/1.1 200 OK\r\n\r\n{ \"next_token\": \"g\", \"resources\": [\"d\", \"e\", \"f\"] }",
48577 "HTTP/1.1 200 OK\r\n\r\n{ \"next_token\": \"\", \"resources\": [] }"
48578 ));
48579
48580 $ri = new MockCommandIterator($this->getServiceBuilder()->get('mock')->getCommand('iterable_command'));
48581
48582 // Ensure that the iterator can be used as KVP array
48583 $data = $ri->toArray();
48584
48585 // Ensure that the iterate is countable
48586 $this->assertEquals(3, count($ri));
48587 $this->assertEquals(array('d', 'e', 'f'), $data);
48588
48589 $this->assertEquals(2, $ri->getRequestCount());
48590 }
48591
48592 public function testHoldsDataOptions()
48593 {
48594 $ri = new MockCommandIterator($this->getServiceBuilder()->get('mock')->getCommand('iterable_command'));
48595 $this->assertNull($ri->get('foo'));
48596 $this->assertSame($ri, $ri->set('foo', 'bar'));
48597 $this->assertEquals('bar', $ri->get('foo'));
48598 }
48599
48600 public function testSettingLimitOrPageSizeClearsData()
48601 {
48602 $this->getServer()->flush();
48603 $this->getServer()->enqueue(array(
48604 "HTTP/1.1 200 OK\r\n\r\n{ \"next_token\": \"\", \"resources\": [\"d\", \"e\", \"f\"] }",
48605 "HTTP/1.1 200 OK\r\n\r\n{ \"next_token\": \"\", \"resources\": [\"d\", \"e\", \"f\"] }",
48606 "HTTP/1.1 200 OK\r\n\r\n{ \"next_token\": \"\", \"resources\": [\"d\", \"e\", \"f\"] }"
48607 ));
48608
48609 $ri = new MockCommandIterator($this->getServiceBuilder()->get('mock')->getCommand('iterable_command'));
48610 $ri->toArray();
48611 $this->assertNotEmpty($this->readAttribute($ri, 'resources'));
48612
48613 $ri->setLimit(10);
48614 $this->assertEmpty($this->readAttribute($ri, 'resources'));
48615
48616 $ri->toArray();
48617 $this->assertNotEmpty($this->readAttribute($ri, 'resources'));
48618 $ri->setPageSize(10);
48619 $this->assertEmpty($this->readAttribute($ri, 'resources'));
48620 }
48621
48622 public function testWorksWithCustomAppendIterator()
48623 {
48624 $this->getServer()->flush();
48625 $this->getServer()->enqueue(array(
48626 "HTTP/1.1 200 OK\r\n\r\n{ \"next_token\": \"\", \"resources\": [\"d\", \"e\", \"f\"] }"
48627 ));
48628 $ri = new MockCommandIterator($this->getServiceBuilder()->get('mock')->getCommand('iterable_command'));
48629 $a = new \Guzzle\Iterator\AppendIterator();
48630 $a->append($ri);
48631 $results = iterator_to_array($a, false);
48632 $this->assertEquals(4, $ri->calledNext);
48633 }
48634 }
48635 <?php
48636
48637 namespace Guzzle\Tests\Service\Resource;
48638
48639 use Guzzle\Service\Resource\ResourceIteratorClassFactory;
48640 use Guzzle\Tests\Service\Mock\Command\MockCommand;
48641
48642 /**
48643 * @covers Guzzle\Service\Resource\ResourceIteratorClassFactory
48644 * @covers Guzzle\Service\Resource\AbstractResourceIteratorFactory
48645 */
48646 class ResourceIteratorClassFactoryTest extends \Guzzle\Tests\GuzzleTestCase
48647 {
48648 /**
48649 * @expectedException \InvalidArgumentException
48650 * @expectedExceptionMessage Iterator was not found for mock_command
48651 */
48652 public function testEnsuresIteratorClassExists()
48653 {
48654 $factory = new ResourceIteratorClassFactory(array('Foo', 'Bar'));
48655 $factory->registerNamespace('Baz');
48656 $command = new MockCommand();
48657 $factory->build($command);
48658 }
48659
48660 public function testBuildsResourceIterators()
48661 {
48662 $factory = new ResourceIteratorClassFactory('Guzzle\Tests\Service\Mock\Model');
48663 $command = new MockCommand();
48664 $iterator = $factory->build($command, array('client.namespace' => 'Guzzle\Tests\Service\Mock'));
48665 $this->assertInstanceOf('Guzzle\Tests\Service\Mock\Model\MockCommandIterator', $iterator);
48666 }
48667
48668 public function testChecksIfCanBuild()
48669 {
48670 $factory = new ResourceIteratorClassFactory('Guzzle\Tests\Service');
48671 $this->assertFalse($factory->canBuild(new MockCommand()));
48672 $factory = new ResourceIteratorClassFactory('Guzzle\Tests\Service\Mock\Model');
48673 $this->assertTrue($factory->canBuild(new MockCommand()));
48674 }
48675 }
48676 <?php
48677
48678 namespace Guzzle\Tests\Service\Resource;
48679
48680 use Guzzle\Service\Resource\CompositeResourceIteratorFactory;
48681 use Guzzle\Service\Resource\ResourceIteratorClassFactory;
48682 use Guzzle\Tests\Service\Mock\Command\MockCommand;
48683
48684 /**
48685 * @covers Guzzle\Service\Resource\CompositeResourceIteratorFactory
48686 */
48687 class CompositeResourceIteratorFactoryTest extends \Guzzle\Tests\GuzzleTestCase
48688 {
48689 /**
48690 * @expectedException \InvalidArgumentException
48691 * @expectedExceptionMessage Iterator was not found for mock_command
48692 */
48693 public function testEnsuresIteratorClassExists()
48694 {
48695 $factory = new CompositeResourceIteratorFactory(array(
48696 new ResourceIteratorClassFactory(array('Foo', 'Bar'))
48697 ));
48698 $cmd = new MockCommand();
48699 $this->assertFalse($factory->canBuild($cmd));
48700 $factory->build($cmd);
48701 }
48702
48703 public function testBuildsResourceIterators()
48704 {
48705 $f1 = new ResourceIteratorClassFactory('Guzzle\Tests\Service\Mock\Model');
48706 $factory = new CompositeResourceIteratorFactory(array());
48707 $factory->addFactory($f1);
48708 $command = new MockCommand();
48709 $iterator = $factory->build($command, array('client.namespace' => 'Guzzle\Tests\Service\Mock'));
48710 $this->assertInstanceOf('Guzzle\Tests\Service\Mock\Model\MockCommandIterator', $iterator);
48711 }
48712 }
48713 <?php
48714
48715 namespace Guzzle\Tests\Service\Resource;
48716
48717 use Guzzle\Service\Resource\MapResourceIteratorFactory;
48718 use Guzzle\Tests\Service\Mock\Command\MockCommand;
48719
48720 /**
48721 * @covers Guzzle\Service\Resource\MapResourceIteratorFactory
48722 */
48723 class MapResourceIteratorFactoryTest extends \Guzzle\Tests\GuzzleTestCase
48724 {
48725 /**
48726 * @expectedException InvalidArgumentException
48727 * @expectedExceptionMessage Iterator was not found for mock_command
48728 */
48729 public function testEnsuresIteratorClassExists()
48730 {
48731 $factory = new MapResourceIteratorFactory(array('Foo', 'Bar'));
48732 $factory->build(new MockCommand());
48733 }
48734
48735 public function testBuildsResourceIterators()
48736 {
48737 $factory = new MapResourceIteratorFactory(array(
48738 'mock_command' => 'Guzzle\Tests\Service\Mock\Model\MockCommandIterator'
48739 ));
48740 $iterator = $factory->build(new MockCommand());
48741 $this->assertInstanceOf('Guzzle\Tests\Service\Mock\Model\MockCommandIterator', $iterator);
48742 }
48743
48744 public function testUsesWildcardMappings()
48745 {
48746 $factory = new MapResourceIteratorFactory(array(
48747 '*' => 'Guzzle\Tests\Service\Mock\Model\MockCommandIterator'
48748 ));
48749 $iterator = $factory->build(new MockCommand());
48750 $this->assertInstanceOf('Guzzle\Tests\Service\Mock\Model\MockCommandIterator', $iterator);
48751 }
48752 }
48753 <?php
48754
48755 namespace Guzzle\Tests\Service\Exception;
48756
48757 use Guzzle\Service\Exception\InconsistentClientTransferException;
48758
48759 class InconsistentClientTransferExceptionTest extends \Guzzle\Tests\GuzzleTestCase
48760 {
48761 public function testStoresCommands()
48762 {
48763 $items = array('foo', 'bar');
48764 $e = new InconsistentClientTransferException($items);
48765 $this->assertEquals($items, $e->getCommands());
48766 }
48767 }
48768 <?php
48769
48770 namespace Guzzle\Tests\Service\Exception;
48771
48772 use Guzzle\Service\Exception\ValidationException;
48773
48774 class ValidationExceptionTest extends \Guzzle\Tests\GuzzleTestCase
48775 {
48776 public function testCanSetAndRetrieveErrors()
48777 {
48778 $errors = array('foo', 'bar');
48779
48780 $e = new ValidationException('Foo');
48781 $e->setErrors($errors);
48782 $this->assertEquals($errors, $e->getErrors());
48783 }
48784 }
48785 <?php
48786
48787 namespace Guzzle\Tests\Service\Exception;
48788
48789 use Guzzle\Http\Exception\MultiTransferException;
48790 use Guzzle\Http\Message\Request;
48791 use Guzzle\Service\Exception\CommandTransferException;
48792 use Guzzle\Tests\Service\Mock\Command\MockCommand;
48793
48794 /**
48795 * @covers Guzzle\Service\Exception\CommandTransferException
48796 */
48797 class CommandTransferExceptionTest extends \Guzzle\Tests\GuzzleTestCase
48798 {
48799 public function testStoresCommands()
48800 {
48801 $c1 = new MockCommand();
48802 $c2 = new MockCommand();
48803 $e = new CommandTransferException('Test');
48804 $e->addSuccessfulCommand($c1)->addFailedCommand($c2);
48805 $this->assertSame(array($c1), $e->getSuccessfulCommands());
48806 $this->assertSame(array($c2), $e->getFailedCommands());
48807 $this->assertSame(array($c1, $c2), $e->getAllCommands());
48808 }
48809
48810 public function testConvertsMultiExceptionIntoCommandTransfer()
48811 {
48812 $r1 = new Request('GET', 'http://foo.com');
48813 $r2 = new Request('GET', 'http://foobaz.com');
48814 $e = new MultiTransferException('Test', 123);
48815 $e->addSuccessfulRequest($r1)->addFailedRequest($r2);
48816 $ce = CommandTransferException::fromMultiTransferException($e);
48817
48818 $this->assertInstanceOf('Guzzle\Service\Exception\CommandTransferException', $ce);
48819 $this->assertEquals('Test', $ce->getMessage());
48820 $this->assertEquals(123, $ce->getCode());
48821 $this->assertSame(array($r1), $ce->getSuccessfulRequests());
48822 $this->assertSame(array($r2), $ce->getFailedRequests());
48823 }
48824
48825 public function testCanRetrieveExceptionForCommand()
48826 {
48827 $r1 = new Request('GET', 'http://foo.com');
48828 $e1 = new \Exception('foo');
48829 $c1 = $this->getMockBuilder('Guzzle\Tests\Service\Mock\Command\MockCommand')
48830 ->setMethods(array('getRequest'))
48831 ->getMock();
48832 $c1->expects($this->once())->method('getRequest')->will($this->returnValue($r1));
48833
48834 $e = new MultiTransferException('Test', 123);
48835 $e->addFailedRequestWithException($r1, $e1);
48836 $ce = CommandTransferException::fromMultiTransferException($e);
48837 $ce->addFailedCommand($c1);
48838
48839 $this->assertSame($e1, $ce->getExceptionForFailedCommand($c1));
48840 }
48841
48842 public function testAddsNonRequestExceptions()
48843 {
48844 $e = new MultiTransferException();
48845 $e->add(new \Exception('bar'));
48846 $e->addFailedRequestWithException(new Request('GET', 'http://www.foo.com'), new \Exception('foo'));
48847 $ce = CommandTransferException::fromMultiTransferException($e);
48848 $this->assertEquals(2, count($ce));
48849 }
48850 }
48851 <?php
48852
48853 namespace Guzzle\Tests\Service;
48854
48855 use Guzzle\Inflection\Inflector;
48856 use Guzzle\Http\Message\Response;
48857 use Guzzle\Plugin\Mock\MockPlugin;
48858 use Guzzle\Service\Description\Operation;
48859 use Guzzle\Service\Client;
48860 use Guzzle\Service\Exception\CommandTransferException;
48861 use Guzzle\Service\Description\ServiceDescription;
48862 use Guzzle\Tests\Service\Mock\Command\MockCommand;
48863 use Guzzle\Service\Resource\ResourceIteratorClassFactory;
48864 use Guzzle\Service\Command\AbstractCommand;
48865
48866 /**
48867 * @group server
48868 * @covers Guzzle\Service\Client
48869 */
48870 class ClientTest extends \Guzzle\Tests\GuzzleTestCase
48871 {
48872 protected $service;
48873 protected $serviceTest;
48874
48875 public function setUp()
48876 {
48877 $this->serviceTest = new ServiceDescription(array(
48878 'test_command' => new Operation(array(
48879 'doc' => 'documentationForCommand',
48880 'method' => 'DELETE',
48881 'class' => 'Guzzle\\Tests\\Service\\Mock\\Command\\MockCommand',
48882 'args' => array(
48883 'bucket' => array(
48884 'required' => true
48885 ),
48886 'key' => array(
48887 'required' => true
48888 )
48889 )
48890 ))
48891 ));
48892
48893 $this->service = ServiceDescription::factory(__DIR__ . '/../TestData/test_service.json');
48894 }
48895
48896 public function testAllowsCustomClientParameters()
48897 {
48898 $client = new Mock\MockClient(null, array(
48899 Client::COMMAND_PARAMS => array(AbstractCommand::RESPONSE_PROCESSING => 'foo')
48900 ));
48901 $command = $client->getCommand('mock_command');
48902 $this->assertEquals('foo', $command->get(AbstractCommand::RESPONSE_PROCESSING));
48903 }
48904
48905 public function testFactoryCreatesClient()
48906 {
48907 $client = Client::factory(array(
48908 'base_url' => 'http://www.test.com/',
48909 'test' => '123'
48910 ));
48911
48912 $this->assertEquals('http://www.test.com/', $client->getBaseUrl());
48913 $this->assertEquals('123', $client->getConfig('test'));
48914 }
48915
48916 public function testFactoryDoesNotRequireBaseUrl()
48917 {
48918 $client = Client::factory();
48919 }
48920
48921 public function testDescribesEvents()
48922 {
48923 $this->assertInternalType('array', Client::getAllEvents());
48924 }
48925
48926 public function testExecutesCommands()
48927 {
48928 $this->getServer()->flush();
48929 $this->getServer()->enqueue("HTTP/1.1 200 OK\r\nContent-Length: 0\r\n\r\n");
48930
48931 $client = new Client($this->getServer()->getUrl());
48932 $cmd = new MockCommand();
48933 $client->execute($cmd);
48934
48935 $this->assertInstanceOf('Guzzle\\Http\\Message\\Response', $cmd->getResponse());
48936 $this->assertInstanceOf('Guzzle\\Http\\Message\\Response', $cmd->getResult());
48937 $this->assertEquals(1, count($this->getServer()->getReceivedRequests(false)));
48938 }
48939
48940 public function testExecutesCommandsWithArray()
48941 {
48942 $client = new Client('http://www.test.com/');
48943 $client->getEventDispatcher()->addSubscriber(new MockPlugin(array(
48944 new Response(200),
48945 new Response(200)
48946 )));
48947
48948 // Create a command set and a command
48949 $set = array(new MockCommand(), new MockCommand());
48950 $client->execute($set);
48951
48952 // Make sure it sent
48953 $this->assertTrue($set[0]->isExecuted());
48954 $this->assertTrue($set[1]->isExecuted());
48955 }
48956
48957 /**
48958 * @expectedException Guzzle\Common\Exception\InvalidArgumentException
48959 */
48960 public function testThrowsExceptionWhenInvalidCommandIsExecuted()
48961 {
48962 $client = new Client();
48963 $client->execute(new \stdClass());
48964 }
48965
48966 /**
48967 * @expectedException InvalidArgumentException
48968 */
48969 public function testThrowsExceptionWhenMissingCommand()
48970 {
48971 $client = new Client();
48972
48973 $mock = $this->getMock('Guzzle\\Service\\Command\\Factory\\FactoryInterface');
48974 $mock->expects($this->any())
48975 ->method('factory')
48976 ->with($this->equalTo('test'))
48977 ->will($this->returnValue(null));
48978
48979 $client->setCommandFactory($mock);
48980 $client->getCommand('test');
48981 }
48982
48983 public function testCreatesCommandsUsingCommandFactory()
48984 {
48985 $mockCommand = new MockCommand();
48986
48987 $client = new Mock\MockClient();
48988 $mock = $this->getMock('Guzzle\\Service\\Command\\Factory\\FactoryInterface');
48989 $mock->expects($this->any())
48990 ->method('factory')
48991 ->with($this->equalTo('foo'))
48992 ->will($this->returnValue($mockCommand));
48993
48994 $client->setCommandFactory($mock);
48995
48996 $command = $client->getCommand('foo', array('acl' => '123'));
48997 $this->assertSame($mockCommand, $command);
48998 $command = $client->getCommand('foo', array('acl' => '123'));
48999 $this->assertSame($mockCommand, $command);
49000 $this->assertSame($client, $command->getClient());
49001 }
49002
49003 public function testOwnsServiceDescription()
49004 {
49005 $client = new Mock\MockClient();
49006 $this->assertNull($client->getDescription());
49007
49008 $description = $this->getMock('Guzzle\\Service\\Description\\ServiceDescription');
49009 $this->assertSame($client, $client->setDescription($description));
49010 $this->assertSame($description, $client->getDescription());
49011 }
49012
49013 public function testOwnsResourceIteratorFactory()
49014 {
49015 $client = new Mock\MockClient();
49016
49017 $method = new \ReflectionMethod($client, 'getResourceIteratorFactory');
49018 $method->setAccessible(TRUE);
49019 $rf1 = $method->invoke($client);
49020
49021 $rf = $this->readAttribute($client, 'resourceIteratorFactory');
49022 $this->assertInstanceOf('Guzzle\\Service\\Resource\\ResourceIteratorClassFactory', $rf);
49023 $this->assertSame($rf1, $rf);
49024
49025 $rf = new ResourceIteratorClassFactory('Guzzle\Tests\Service\Mock');
49026 $client->setResourceIteratorFactory($rf);
49027 $this->assertNotSame($rf1, $rf);
49028 }
49029
49030 public function testClientResetsRequestsBeforeExecutingCommands()
49031 {
49032 $this->getServer()->flush();
49033 $this->getServer()->enqueue(array(
49034 "HTTP/1.1 200 OK\r\nContent-Length: 2\r\n\r\nHi",
49035 "HTTP/1.1 200 OK\r\nContent-Length: 1\r\n\r\nI"
49036 ));
49037
49038 $client = new Mock\MockClient($this->getServer()->getUrl());
49039
49040 $command = $client->getCommand('mock_command');
49041 $client->execute($command);
49042 $client->execute($command);
49043 $this->assertEquals('I', $command->getResponse()->getBody(true));
49044 }
49045
49046 public function testClientCreatesIterators()
49047 {
49048 $client = new Mock\MockClient();
49049
49050 $iterator = $client->getIterator('mock_command', array(
49051 'foo' => 'bar'
49052 ), array(
49053 'limit' => 10
49054 ));
49055
49056 $this->assertInstanceOf('Guzzle\Tests\Service\Mock\Model\MockCommandIterator', $iterator);
49057 $this->assertEquals(10, $this->readAttribute($iterator, 'limit'));
49058
49059 $command = $this->readAttribute($iterator, 'originalCommand');
49060 $this->assertEquals('bar', $command->get('foo'));
49061 }
49062
49063 public function testClientCreatesIteratorsWithNoOptions()
49064 {
49065 $client = new Mock\MockClient();
49066 $iterator = $client->getIterator('mock_command');
49067 $this->assertInstanceOf('Guzzle\Tests\Service\Mock\Model\MockCommandIterator', $iterator);
49068 }
49069
49070 public function testClientCreatesIteratorsWithCommands()
49071 {
49072 $client = new Mock\MockClient();
49073 $command = new MockCommand();
49074 $iterator = $client->getIterator($command);
49075 $this->assertInstanceOf('Guzzle\Tests\Service\Mock\Model\MockCommandIterator', $iterator);
49076 $iteratorCommand = $this->readAttribute($iterator, 'originalCommand');
49077 $this->assertSame($command, $iteratorCommand);
49078 }
49079
49080 public function testClientHoldsInflector()
49081 {
49082 $client = new Mock\MockClient();
49083 $this->assertInstanceOf('Guzzle\Inflection\MemoizingInflector', $client->getInflector());
49084
49085 $inflector = new Inflector();
49086 $client->setInflector($inflector);
49087 $this->assertSame($inflector, $client->getInflector());
49088 }
49089
49090 public function testClientAddsGlobalCommandOptions()
49091 {
49092 $client = new Mock\MockClient('http://www.foo.com', array(
49093 Client::COMMAND_PARAMS => array(
49094 'mesa' => 'bar'
49095 )
49096 ));
49097 $command = $client->getCommand('mock_command');
49098 $this->assertEquals('bar', $command->get('mesa'));
49099 }
49100
49101 public function testSupportsServiceDescriptionBaseUrls()
49102 {
49103 $description = new ServiceDescription(array('baseUrl' => 'http://foo.com'));
49104 $client = new Client();
49105 $client->setDescription($description);
49106 $this->assertEquals('http://foo.com', $client->getBaseUrl());
49107 }
49108
49109 public function testMergesDefaultCommandParamsCorrectly()
49110 {
49111 $client = new Mock\MockClient('http://www.foo.com', array(
49112 Client::COMMAND_PARAMS => array(
49113 'mesa' => 'bar',
49114 'jar' => 'jar'
49115 )
49116 ));
49117 $command = $client->getCommand('mock_command', array('jar' => 'test'));
49118 $this->assertEquals('bar', $command->get('mesa'));
49119 $this->assertEquals('test', $command->get('jar'));
49120 }
49121
49122 /**
49123 * @expectedException \Guzzle\Http\Exception\BadResponseException
49124 */
49125 public function testWrapsSingleCommandExceptions()
49126 {
49127 $client = new Mock\MockClient('http://foobaz.com');
49128 $mock = new MockPlugin(array(new Response(401)));
49129 $client->addSubscriber($mock);
49130 $client->execute(new MockCommand());
49131 }
49132
49133 public function testWrapsMultipleCommandExceptions()
49134 {
49135 $client = new Mock\MockClient('http://foobaz.com');
49136 $mock = new MockPlugin(array(new Response(200), new Response(200), new Response(404), new Response(500)));
49137 $client->addSubscriber($mock);
49138
49139 $cmds = array(new MockCommand(), new MockCommand(), new MockCommand(), new MockCommand());
49140 try {
49141 $client->execute($cmds);
49142 } catch (CommandTransferException $e) {
49143 $this->assertEquals(2, count($e->getFailedRequests()));
49144 $this->assertEquals(2, count($e->getSuccessfulRequests()));
49145 $this->assertEquals(2, count($e->getFailedCommands()));
49146 $this->assertEquals(2, count($e->getSuccessfulCommands()));
49147
49148 foreach ($e->getSuccessfulCommands() as $c) {
49149 $this->assertTrue($c->getResponse()->isSuccessful());
49150 }
49151
49152 foreach ($e->getFailedCommands() as $c) {
49153 $this->assertFalse($c->getRequest()->getResponse()->isSuccessful());
49154 }
49155 }
49156 }
49157 }
49158 <?php
49159
49160 namespace Guzzle\Tests\Service\Builder;
49161
49162 use Guzzle\Service\Builder\ServiceBuilderLoader;
49163
49164 /**
49165 * @covers Guzzle\Service\Builder\ServiceBuilderLoader
49166 */
49167 class ServiceBuilderLoaderTest extends \Guzzle\Tests\GuzzleTestCase
49168 {
49169 public function testBuildsServiceBuilders()
49170 {
49171 $arrayFactory = new ServiceBuilderLoader();
49172
49173 $data = array(
49174 'services' => array(
49175 'abstract' => array(
49176 'params' => array(
49177 'access_key' => 'xyz',
49178 'secret' => 'abc',
49179 ),
49180 ),
49181 'foo' => array(
49182 'extends' => 'abstract',
49183 'params' => array(
49184 'baz' => 'bar',
49185 ),
49186 ),
49187 'mock' => array(
49188 'extends' => 'abstract',
49189 'params' => array(
49190 'username' => 'foo',
49191 'password' => 'baz',
49192 'subdomain' => 'bar',
49193 )
49194 )
49195 )
49196 );
49197
49198 $builder = $arrayFactory->load($data);
49199
49200 // Ensure that services were parsed
49201 $this->assertTrue(isset($builder['mock']));
49202 $this->assertTrue(isset($builder['abstract']));
49203 $this->assertTrue(isset($builder['foo']));
49204 $this->assertFalse(isset($builder['jimmy']));
49205 }
49206
49207 /**
49208 * @expectedException Guzzle\Service\Exception\ServiceNotFoundException
49209 * @expectedExceptionMessage foo is trying to extend a non-existent service: abstract
49210 */
49211 public function testThrowsExceptionWhenExtendingNonExistentService()
49212 {
49213 $arrayFactory = new ServiceBuilderLoader();
49214
49215 $data = array(
49216 'services' => array(
49217 'foo' => array(
49218 'extends' => 'abstract'
49219 )
49220 )
49221 );
49222
49223 $builder = $arrayFactory->load($data);
49224 }
49225
49226 public function testAllowsGlobalParameterOverrides()
49227 {
49228 $arrayFactory = new ServiceBuilderLoader();
49229
49230 $data = array(
49231 'services' => array(
49232 'foo' => array(
49233 'params' => array(
49234 'foo' => 'baz',
49235 'bar' => 'boo'
49236 )
49237 )
49238 )
49239 );
49240
49241 $builder = $arrayFactory->load($data, array(
49242 'bar' => 'jar',
49243 'far' => 'car'
49244 ));
49245
49246 $compiled = json_decode($builder->serialize(), true);
49247 $this->assertEquals(array(
49248 'foo' => 'baz',
49249 'bar' => 'jar',
49250 'far' => 'car'
49251 ), $compiled['foo']['params']);
49252 }
49253
49254 public function tstDoesNotErrorOnCircularReferences()
49255 {
49256 $arrayFactory = new ServiceBuilderLoader();
49257 $arrayFactory->load(array(
49258 'services' => array(
49259 'too' => array('extends' => 'ball'),
49260 'ball' => array('extends' => 'too'),
49261 )
49262 ));
49263 }
49264
49265 public function configProvider()
49266 {
49267 $foo = array(
49268 'extends' => 'bar',
49269 'class' => 'stdClass',
49270 'params' => array('a' => 'test', 'b' => '456')
49271 );
49272
49273 return array(
49274 array(
49275 // Does not extend the existing `foo` service but overwrites it
49276 array(
49277 'services' => array(
49278 'foo' => $foo,
49279 'bar' => array('params' => array('baz' => '123'))
49280 )
49281 ),
49282 array(
49283 'services' => array(
49284 'foo' => array('class' => 'Baz')
49285 )
49286 ),
49287 array(
49288 'services' => array(
49289 'foo' => array('class' => 'Baz'),
49290 'bar' => array('params' => array('baz' => '123'))
49291 )
49292 )
49293 ),
49294 array(
49295 // Extends the existing `foo` service
49296 array(
49297 'services' => array(
49298 'foo' => $foo,
49299 'bar' => array('params' => array('baz' => '123'))
49300 )
49301 ),
49302 array(
49303 'services' => array(
49304 'foo' => array(
49305 'extends' => 'foo',
49306 'params' => array('b' => '123', 'c' => 'def')
49307 )
49308 )
49309 ),
49310 array(
49311 'services' => array(
49312 'foo' => array(
49313 'extends' => 'bar',
49314 'class' => 'stdClass',
49315 'params' => array('a' => 'test', 'b' => '123', 'c' => 'def')
49316 ),
49317 'bar' => array('params' => array('baz' => '123'))
49318 )
49319 )
49320 )
49321 );
49322 }
49323
49324 /**
49325 * @dataProvider configProvider
49326 */
49327 public function testCombinesConfigs($a, $b, $c)
49328 {
49329 $l = new ServiceBuilderLoader();
49330 $m = new \ReflectionMethod($l, 'mergeData');
49331 $m->setAccessible(true);
49332 $this->assertEquals($c, $m->invoke($l, $a, $b));
49333 }
49334 }
49335 <?php
49336
49337 namespace Guzzle\Tests\Service;
49338
49339 use Guzzle\Plugin\History\HistoryPlugin;
49340 use Guzzle\Service\Builder\ServiceBuilder;
49341 use Guzzle\Service\Client;
49342
49343 /**
49344 * @covers Guzzle\Service\Builder\ServiceBuilder
49345 */
49346 class ServiceBuilderTest extends \Guzzle\Tests\GuzzleTestCase
49347 {
49348 protected $arrayData = array(
49349 'michael.mock' => array(
49350 'class' => 'Guzzle\Tests\Service\Mock\MockClient',
49351 'params' => array(
49352 'username' => 'michael',
49353 'password' => 'testing123',
49354 'subdomain' => 'michael',
49355 ),
49356 ),
49357 'billy.mock' => array(
49358 'alias' => 'Hello!',
49359 'class' => 'Guzzle\Tests\Service\Mock\MockClient',
49360 'params' => array(
49361 'username' => 'billy',
49362 'password' => 'passw0rd',
49363 'subdomain' => 'billy',
49364 ),
49365 ),
49366 'billy.testing' => array(
49367 'extends' => 'billy.mock',
49368 'params' => array(
49369 'subdomain' => 'test.billy',
49370 ),
49371 ),
49372 'missing_params' => array(
49373 'extends' => 'billy.mock'
49374 )
49375 );
49376
49377 public function testAllowsSerialization()
49378 {
49379 $builder = ServiceBuilder::factory($this->arrayData);
49380 $cached = unserialize(serialize($builder));
49381 $this->assertEquals($cached, $builder);
49382 }
49383
49384 public function testDelegatesFactoryMethodToAbstractFactory()
49385 {
49386 $builder = ServiceBuilder::factory($this->arrayData);
49387 $c = $builder->get('michael.mock');
49388 $this->assertInstanceOf('Guzzle\Tests\Service\Mock\MockClient', $c);
49389 }
49390
49391 /**
49392 * @expectedException Guzzle\Service\Exception\ServiceNotFoundException
49393 * @expectedExceptionMessage No service is registered as foobar
49394 */
49395 public function testThrowsExceptionWhenGettingInvalidClient()
49396 {
49397 ServiceBuilder::factory($this->arrayData)->get('foobar');
49398 }
49399
49400 public function testStoresClientCopy()
49401 {
49402 $builder = ServiceBuilder::factory($this->arrayData);
49403 $client = $builder->get('michael.mock');
49404 $this->assertInstanceOf('Guzzle\Tests\Service\Mock\MockClient', $client);
49405 $this->assertEquals('http://127.0.0.1:8124/v1/michael', $client->getBaseUrl());
49406 $this->assertEquals($client, $builder->get('michael.mock'));
49407
49408 // Get another client but throw this one away
49409 $client2 = $builder->get('billy.mock', true);
49410 $this->assertInstanceOf('Guzzle\Tests\Service\Mock\MockClient', $client2);
49411 $this->assertEquals('http://127.0.0.1:8124/v1/billy', $client2->getBaseUrl());
49412
49413 // Make sure the original client is still there and set
49414 $this->assertTrue($client === $builder->get('michael.mock'));
49415
49416 // Create a new billy.mock client that is stored
49417 $client3 = $builder->get('billy.mock');
49418
49419 // Make sure that the stored billy.mock client is equal to the other stored client
49420 $this->assertTrue($client3 === $builder->get('billy.mock'));
49421
49422 // Make sure that this client is not equal to the previous throwaway client
49423 $this->assertFalse($client2 === $builder->get('billy.mock'));
49424 }
49425
49426 public function testBuildersPassOptionsThroughToClients()
49427 {
49428 $s = new ServiceBuilder(array(
49429 'michael.mock' => array(
49430 'class' => 'Guzzle\Tests\Service\Mock\MockClient',
49431 'params' => array(
49432 'base_url' => 'http://www.test.com/',
49433 'subdomain' => 'michael',
49434 'password' => 'test',
49435 'username' => 'michael',
49436 'curl.curlopt_proxyport' => 8080
49437 )
49438 )
49439 ));
49440
49441 $c = $s->get('michael.mock');
49442 $this->assertEquals(8080, $c->getConfig('curl.curlopt_proxyport'));
49443 }
49444
49445 public function testUsesTheDefaultBuilderWhenNoBuilderIsSpecified()
49446 {
49447 $s = new ServiceBuilder(array(
49448 'michael.mock' => array(
49449 'class' => 'Guzzle\Tests\Service\Mock\MockClient',
49450 'params' => array(
49451 'base_url' => 'http://www.test.com/',
49452 'subdomain' => 'michael',
49453 'password' => 'test',
49454 'username' => 'michael',
49455 'curl.curlopt_proxyport' => 8080
49456 )
49457 )
49458 ));
49459
49460 $c = $s->get('michael.mock');
49461 $this->assertInstanceOf('Guzzle\Tests\Service\Mock\MockClient', $c);
49462 }
49463
49464 public function testUsedAsArray()
49465 {
49466 $b = ServiceBuilder::factory($this->arrayData);
49467 $this->assertTrue($b->offsetExists('michael.mock'));
49468 $this->assertFalse($b->offsetExists('not_there'));
49469 $this->assertInstanceOf('Guzzle\Service\Client', $b['michael.mock']);
49470
49471 unset($b['michael.mock']);
49472 $this->assertFalse($b->offsetExists('michael.mock'));
49473
49474 $b['michael.mock'] = new Client('http://www.test.com/');
49475 $this->assertInstanceOf('Guzzle\Service\Client', $b['michael.mock']);
49476 }
49477
49478 public function testFactoryCanCreateFromJson()
49479 {
49480 $tmp = sys_get_temp_dir() . '/test.js';
49481 file_put_contents($tmp, json_encode($this->arrayData));
49482 $b = ServiceBuilder::factory($tmp);
49483 unlink($tmp);
49484 $s = $b->get('billy.testing');
49485 $this->assertEquals('test.billy', $s->getConfig('subdomain'));
49486 $this->assertEquals('billy', $s->getConfig('username'));
49487 }
49488
49489 public function testFactoryCanCreateFromArray()
49490 {
49491 $b = ServiceBuilder::factory($this->arrayData);
49492 $s = $b->get('billy.testing');
49493 $this->assertEquals('test.billy', $s->getConfig('subdomain'));
49494 $this->assertEquals('billy', $s->getConfig('username'));
49495 }
49496
49497 public function testFactoryDoesNotRequireParams()
49498 {
49499 $b = ServiceBuilder::factory($this->arrayData);
49500 $s = $b->get('missing_params');
49501 $this->assertEquals('billy', $s->getConfig('username'));
49502 }
49503
49504 public function testBuilderAllowsReferencesBetweenClients()
49505 {
49506 $builder = ServiceBuilder::factory(array(
49507 'a' => array(
49508 'class' => 'Guzzle\Tests\Service\Mock\MockClient',
49509 'params' => array(
49510 'other_client' => '{b}',
49511 'username' => 'x',
49512 'password' => 'y',
49513 'subdomain' => 'z'
49514 )
49515 ),
49516 'b' => array(
49517 'class' => 'Guzzle\Tests\Service\Mock\MockClient',
49518 'params' => array(
49519 'username' => '1',
49520 'password' => '2',
49521 'subdomain' => '3'
49522 )
49523 )
49524 ));
49525
49526 $client = $builder['a'];
49527 $this->assertEquals('x', $client->getConfig('username'));
49528 $this->assertSame($builder['b'], $client->getConfig('other_client'));
49529 $this->assertEquals('1', $builder['b']->getConfig('username'));
49530 }
49531
49532 public function testEmitsEventsWhenClientsAreCreated()
49533 {
49534 // Ensure that the client signals that it emits an event
49535 $this->assertEquals(array('service_builder.create_client'), ServiceBuilder::getAllEvents());
49536
49537 // Create a test service builder
49538 $builder = ServiceBuilder::factory(array(
49539 'a' => array(
49540 'class' => 'Guzzle\Tests\Service\Mock\MockClient',
49541 'params' => array(
49542 'username' => 'test',
49543 'password' => '123',
49544 'subdomain' => 'z'
49545 )
49546 )
49547 ));
49548
49549 // Add an event listener to pick up client creation events
49550 $emits = 0;
49551 $builder->getEventDispatcher()->addListener('service_builder.create_client', function($event) use (&$emits) {
49552 $emits++;
49553 });
49554
49555 // Get the 'a' client by name
49556 $client = $builder->get('a');
49557
49558 // Ensure that the event was emitted once, and that the client was present
49559 $this->assertEquals(1, $emits);
49560 $this->assertInstanceOf('Guzzle\Tests\Service\Mock\MockClient', $client);
49561 }
49562
49563 public function testCanAddGlobalParametersToServicesOnLoad()
49564 {
49565 $builder = ServiceBuilder::factory($this->arrayData, array(
49566 'username' => 'fred',
49567 'new_value' => 'test'
49568 ));
49569
49570 $data = json_decode($builder->serialize(), true);
49571
49572 foreach ($data as $service) {
49573 $this->assertEquals('fred', $service['params']['username']);
49574 $this->assertEquals('test', $service['params']['new_value']);
49575 }
49576 }
49577
49578 public function testAddsGlobalPlugins()
49579 {
49580 $b = new ServiceBuilder($this->arrayData);
49581 $b->addGlobalPlugin(new HistoryPlugin());
49582 $s = $b->get('michael.mock');
49583 $this->assertTrue($s->getEventDispatcher()->hasListeners('request.sent'));
49584 }
49585
49586 public function testCanGetData()
49587 {
49588 $b = new ServiceBuilder($this->arrayData);
49589 $this->assertEquals($this->arrayData['michael.mock'], $b->getData('michael.mock'));
49590 $this->assertNull($b->getData('ewofweoweofe'));
49591 }
49592
49593 public function testCanGetByAlias()
49594 {
49595 $b = new ServiceBuilder($this->arrayData);
49596 $this->assertSame($b->get('billy.mock'), $b->get('Hello!'));
49597 }
49598
49599 public function testCanOverwriteParametersForThrowawayClients()
49600 {
49601 $b = new ServiceBuilder($this->arrayData);
49602
49603 $c1 = $b->get('michael.mock');
49604 $this->assertEquals('michael', $c1->getConfig('username'));
49605
49606 $c2 = $b->get('michael.mock', array('username' => 'jeremy'));
49607 $this->assertEquals('jeremy', $c2->getConfig('username'));
49608 }
49609
49610 public function testGettingAThrowawayClientWithParametersDoesNotAffectGettingOtherClients()
49611 {
49612 $b = new ServiceBuilder($this->arrayData);
49613
49614 $c1 = $b->get('michael.mock', array('username' => 'jeremy'));
49615 $this->assertEquals('jeremy', $c1->getConfig('username'));
49616
49617 $c2 = $b->get('michael.mock');
49618 $this->assertEquals('michael', $c2->getConfig('username'));
49619 }
49620
49621 public function testCanUseArbitraryData()
49622 {
49623 $b = new ServiceBuilder();
49624 $b['a'] = 'foo';
49625 $this->assertTrue(isset($b['a']));
49626 $this->assertEquals('foo', $b['a']);
49627 unset($b['a']);
49628 $this->assertFalse(isset($b['a']));
49629 }
49630
49631 public function testCanRegisterServiceData()
49632 {
49633 $b = new ServiceBuilder();
49634 $b['a'] = array(
49635 'class' => 'Guzzle\Tests\Service\Mock\MockClient',
49636 'params' => array(
49637 'username' => 'billy',
49638 'password' => 'passw0rd',
49639 'subdomain' => 'billy',
49640 )
49641 );
49642 $this->assertTrue(isset($b['a']));
49643 $this->assertInstanceOf('Guzzle\Tests\Service\Mock\MockClient', $b['a']);
49644 $client = $b['a'];
49645 unset($b['a']);
49646 $this->assertFalse(isset($b['a']));
49647 // Ensure that instantiated clients can be registered
49648 $b['mock'] = $client;
49649 $this->assertSame($client, $b['mock']);
49650 }
49651 }
49652 <?php
49653
49654 namespace Guzzle\Tests\Service\Mock;
49655
49656 use Guzzle\Common\Collection;
49657 use Guzzle\Service\Client;
49658
49659 /**
49660 * Mock Guzzle Service
49661 */
49662 class MockClient extends Client
49663 {
49664 /**
49665 * Factory method to create a new mock client
49666 *
49667 * @param array|Collection $config Configuration data. Array keys:
49668 * base_url - Base URL of web service
49669 * api_version - API version
49670 * scheme - URI scheme: http or https
49671 * * username - API username
49672 * * password - API password
49673 * * subdomain - Unfuddle account subdomain
49674 *
49675 * @return MockClient
49676 */
49677 public static function factory($config = array())
49678 {
49679 $config = Collection::fromConfig($config, array(
49680 'base_url' => '{scheme}://127.0.0.1:8124/{api_version}/{subdomain}',
49681 'scheme' => 'http',
49682 'api_version' => 'v1'
49683 ), array('username', 'password', 'subdomain'));
49684
49685 return new self($config->get('base_url'), $config);
49686 }
49687 }
49688 <?php
49689
49690 namespace Guzzle\Tests\Service\Mock\Model;
49691
49692 use Guzzle\Service\Resource\ResourceIterator;
49693
49694 class MockCommandIterator extends ResourceIterator
49695 {
49696 public $calledNext = 0;
49697
49698 protected function sendRequest()
49699 {
49700 if ($this->nextToken) {
49701 $this->command->set('next_token', $this->nextToken);
49702 }
49703
49704 $this->command->set('page_size', (int) $this->calculatePageSize());
49705 $this->command->execute();
49706
49707 $data = json_decode($this->command->getResponse()->getBody(true), true);
49708
49709 $this->nextToken = $data['next_token'];
49710
49711 return $data['resources'];
49712 }
49713
49714 public function next()
49715 {
49716 $this->calledNext++;
49717 parent::next();
49718 }
49719
49720 public function getResources()
49721 {
49722 return $this->resources;
49723 }
49724
49725 public function getIteratedCount()
49726 {
49727 return $this->iteratedCount;
49728 }
49729 }
49730 <?php
49731
49732 namespace Guzzle\Tests\Service\Mock\Command\Sub;
49733
49734 use Guzzle\Tests\Service\Mock\Command\MockCommand;
49735
49736 class Sub extends MockCommand {}
49737 <?php
49738
49739 namespace Guzzle\Tests\Service\Mock\Command;
49740
49741 use Guzzle\Service\Description\Operation;
49742
49743 class MockCommand extends \Guzzle\Service\Command\AbstractCommand
49744 {
49745 protected function createOperation()
49746 {
49747 return new Operation(array(
49748 'name' => get_called_class() == __CLASS__ ? 'mock_command' : 'sub.sub',
49749 'httpMethod' => 'POST',
49750 'parameters' => array(
49751 'test' => array(
49752 'default' => 123,
49753 'required' => true,
49754 'doc' => 'Test argument'
49755 ),
49756 '_internal' => array(
49757 'default' => 'abc'
49758 ),
49759 'foo' => array('filters' => array('strtoupper'))
49760 )
49761 ));
49762 }
49763
49764 protected function build()
49765 {
49766 $this->request = $this->client->createRequest();
49767 }
49768 }
49769 <?php
49770
49771 namespace Guzzle\Tests\Service\Mock\Command;
49772
49773 use Guzzle\Service\Description\Operation;
49774
49775 class OtherCommand extends MockCommand
49776 {
49777 protected function createOperation()
49778 {
49779 return new Operation(array(
49780 'name' => 'other_command',
49781 'parameters' => array(
49782 'test' => array(
49783 'default' => '123',
49784 'required' => true,
49785 'doc' => 'Test argument'
49786 ),
49787 'other' => array(),
49788 'arg' => array('type' => 'string'),
49789 'static' => array('static' => true, 'default' => 'this is static')
49790 )
49791 ));
49792 }
49793
49794 protected function build()
49795 {
49796 $this->request = $this->client->getRequest('HEAD');
49797 }
49798 }
49799 <?php
49800
49801 namespace Guzzle\Tests\Service\Mock\Command;
49802
49803 use Guzzle\Service\Description\Operation;
49804
49805 class IterableCommand extends MockCommand
49806 {
49807 protected function createOperation()
49808 {
49809 return new Operation(array(
49810 'name' => 'iterable_command',
49811 'parameters' => array(
49812 'page_size' => array('type' => 'integer'),
49813 'next_token' => array('type' => 'string')
49814 )
49815 ));
49816 }
49817
49818 protected function build()
49819 {
49820 $this->request = $this->client->createRequest('GET');
49821
49822 // Add the next token and page size query string values
49823 $this->request->getQuery()->set('next_token', $this->get('next_token'));
49824
49825 if ($this->get('page_size')) {
49826 $this->request->getQuery()->set('page_size', $this->get('page_size'));
49827 }
49828 }
49829 }
49830 <?php
49831
49832 namespace Guzzle\Tests\Service\Command;
49833
49834 use Guzzle\Service\Command\Factory\MapFactory;
49835
49836 /**
49837 * @covers Guzzle\Service\Command\Factory\MapFactory
49838 */
49839 class MapFactoryTest extends \Guzzle\Tests\GuzzleTestCase
49840 {
49841 public function mapProvider()
49842 {
49843 return array(
49844 array('foo', null),
49845 array('test', 'Guzzle\Tests\Service\Mock\Command\MockCommand'),
49846 array('test1', 'Guzzle\Tests\Service\Mock\Command\OtherCommand')
49847 );
49848 }
49849
49850 /**
49851 * @dataProvider mapProvider
49852 */
49853 public function testCreatesCommandsUsingMappings($key, $result)
49854 {
49855 $factory = new MapFactory(array(
49856 'test' => 'Guzzle\Tests\Service\Mock\Command\MockCommand',
49857 'test1' => 'Guzzle\Tests\Service\Mock\Command\OtherCommand'
49858 ));
49859
49860 if (is_null($result)) {
49861 $this->assertNull($factory->factory($key));
49862 } else {
49863 $this->assertInstanceof($result, $factory->factory($key));
49864 }
49865 }
49866 }
49867 <?php
49868
49869 namespace Guzzle\Tests\Service\Command;
49870
49871 use Guzzle\Tests\Service\Mock\MockClient;
49872 use Guzzle\Service\Command\Factory\ConcreteClassFactory;
49873
49874 /**
49875 * @covers Guzzle\Service\Command\Factory\ConcreteClassFactory
49876 */
49877 class ConcreteClassFactoryTest extends \Guzzle\Tests\GuzzleTestCase
49878 {
49879 public function testProvider()
49880 {
49881 return array(
49882 array('foo', null, 'Guzzle\\Tests\\Service\\Mock\\Command\\'),
49883 array('mock_command', 'Guzzle\Tests\Service\Mock\Command\MockCommand', 'Guzzle\\Tests\\Service\\Mock\\Command\\'),
49884 array('other_command', 'Guzzle\Tests\Service\Mock\Command\OtherCommand', 'Guzzle\\Tests\\Service\\Mock\\Command\\'),
49885 array('sub.sub', 'Guzzle\Tests\Service\Mock\Command\Sub\Sub', 'Guzzle\\Tests\\Service\\Mock\\Command\\'),
49886 array('sub.sub', null, 'Guzzle\\Foo\\'),
49887 array('foo', null, null),
49888 array('mock_command', 'Guzzle\Tests\Service\Mock\Command\MockCommand', null),
49889 array('other_command', 'Guzzle\Tests\Service\Mock\Command\OtherCommand', null),
49890 array('sub.sub', 'Guzzle\Tests\Service\Mock\Command\Sub\Sub', null)
49891 );
49892 }
49893
49894 /**
49895 * @dataProvider testProvider
49896 */
49897 public function testCreatesConcreteCommands($key, $result, $prefix)
49898 {
49899 if (!$prefix) {
49900 $client = new MockClient();
49901 } else {
49902 $client = new MockClient('', array(
49903 'command.prefix' => $prefix
49904 ));
49905 }
49906
49907 $factory = new ConcreteClassFactory($client);
49908
49909 if (is_null($result)) {
49910 $this->assertNull($factory->factory($key));
49911 } else {
49912 $this->assertInstanceof($result, $factory->factory($key));
49913 }
49914 }
49915 }
49916 <?php
49917
49918 namespace Guzzle\Tests\Service\Command;
49919
49920 use Guzzle\Service\Client;
49921 use Guzzle\Service\Command\Factory\AliasFactory;
49922 use Guzzle\Service\Command\Factory\MapFactory;
49923 use Guzzle\Service\Command\Factory\CompositeFactory;
49924
49925 /**
49926 * @covers Guzzle\Service\Command\Factory\AliasFactory
49927 */
49928 class AliasFactoryTest extends \Guzzle\Tests\GuzzleTestCase
49929 {
49930 private $factory;
49931 private $client;
49932
49933 public function setup()
49934 {
49935 $this->client = new Client();
49936
49937 $map = new MapFactory(array(
49938 'test' => 'Guzzle\Tests\Service\Mock\Command\MockCommand',
49939 'test1' => 'Guzzle\Tests\Service\Mock\Command\OtherCommand'
49940 ));
49941
49942 $this->factory = new AliasFactory($this->client, array(
49943 'foo' => 'test',
49944 'bar' => 'sub',
49945 'sub' => 'test1',
49946 'krull' => 'test3',
49947 'krull_2' => 'krull',
49948 'sub_2' => 'bar',
49949 'bad_link' => 'jarjar'
49950 ));
49951
49952 $map2 = new MapFactory(array(
49953 'test3' => 'Guzzle\Tests\Service\Mock\Command\Sub\Sub'
49954 ));
49955
49956 $this->client->setCommandFactory(new CompositeFactory(array($map, $this->factory, $map2)));
49957 }
49958
49959 public function aliasProvider()
49960 {
49961 return array(
49962 array('foo', 'Guzzle\Tests\Service\Mock\Command\MockCommand', false),
49963 array('bar', 'Guzzle\Tests\Service\Mock\Command\OtherCommand', false),
49964 array('sub', 'Guzzle\Tests\Service\Mock\Command\OtherCommand', false),
49965 array('sub_2', 'Guzzle\Tests\Service\Mock\Command\OtherCommand', false),
49966 array('krull', 'Guzzle\Tests\Service\Mock\Command\Sub\Sub', false),
49967 array('krull_2', 'Guzzle\Tests\Service\Mock\Command\Sub\Sub', false),
49968 array('missing', null, true),
49969 array('bad_link', null, true)
49970 );
49971 }
49972
49973 /**
49974 * @dataProvider aliasProvider
49975 */
49976 public function testAliasesCommands($key, $result, $exception)
49977 {
49978 try {
49979 $command = $this->client->getCommand($key);
49980 if (is_null($result)) {
49981 $this->assertNull($command);
49982 } else {
49983 $this->assertInstanceof($result, $command);
49984 }
49985 } catch (\Exception $e) {
49986 if (!$exception) {
49987 $this->fail('Got exception when it was not expected');
49988 }
49989 }
49990 }
49991 }
49992 <?php
49993
49994 namespace Guzzle\Tests\Service\Command;
49995
49996 use Guzzle\Service\Description\ServiceDescription;
49997 use Guzzle\Service\Command\Factory\ServiceDescriptionFactory;
49998 use Guzzle\Inflection\Inflector;
49999
50000 /**
50001 * @covers Guzzle\Service\Command\Factory\ServiceDescriptionFactory
50002 */
50003 class ServiceDescriptionFactoryTest extends \Guzzle\Tests\GuzzleTestCase
50004 {
50005 public function testProvider()
50006 {
50007 return array(
50008 array('foo', null),
50009 array('jar_jar', 'Guzzle\Tests\Service\Mock\Command\MockCommand'),
50010 array('binks', 'Guzzle\Tests\Service\Mock\Command\OtherCommand')
50011 );
50012 }
50013
50014 /**
50015 * @dataProvider testProvider
50016 */
50017 public function testCreatesCommandsUsingServiceDescriptions($key, $result)
50018 {
50019 $d = $this->getDescription();
50020
50021 $factory = new ServiceDescriptionFactory($d);
50022 $this->assertSame($d, $factory->getServiceDescription());
50023
50024 if (is_null($result)) {
50025 $this->assertNull($factory->factory($key));
50026 } else {
50027 $this->assertInstanceof($result, $factory->factory($key));
50028 }
50029 }
50030
50031 public function testUsesUcFirstIfNoExactMatch()
50032 {
50033 $d = $this->getDescription();
50034 $factory = new ServiceDescriptionFactory($d, new Inflector());
50035 $this->assertInstanceof('Guzzle\Tests\Service\Mock\Command\OtherCommand', $factory->factory('Test'));
50036 $this->assertInstanceof('Guzzle\Tests\Service\Mock\Command\OtherCommand', $factory->factory('test'));
50037 }
50038
50039 public function testUsesInflectionIfNoExactMatch()
50040 {
50041 $d = $this->getDescription();
50042 $factory = new ServiceDescriptionFactory($d, new Inflector());
50043 $this->assertInstanceof('Guzzle\Tests\Service\Mock\Command\OtherCommand', $factory->factory('Binks'));
50044 $this->assertInstanceof('Guzzle\Tests\Service\Mock\Command\OtherCommand', $factory->factory('binks'));
50045 $this->assertInstanceof('Guzzle\Tests\Service\Mock\Command\MockCommand', $factory->factory('JarJar'));
50046 $this->assertInstanceof('Guzzle\Tests\Service\Mock\Command\MockCommand', $factory->factory('jar_jar'));
50047 }
50048
50049 protected function getDescription()
50050 {
50051 return ServiceDescription::factory(array(
50052 'operations' => array(
50053 'jar_jar' => array('class' => 'Guzzle\Tests\Service\Mock\Command\MockCommand'),
50054 'binks' => array('class' => 'Guzzle\Tests\Service\Mock\Command\OtherCommand'),
50055 'Test' => array('class' => 'Guzzle\Tests\Service\Mock\Command\OtherCommand')
50056 )
50057 ));
50058 }
50059 }
50060 <?php
50061
50062 namespace Guzzle\Tests\Service\Command;
50063
50064 use Guzzle\Service\Command\Factory\CompositeFactory;
50065
50066 /**
50067 * @covers Guzzle\Service\Command\Factory\CompositeFactory
50068 */
50069 class CompositeFactoryTest extends \Guzzle\Tests\GuzzleTestCase
50070 {
50071 private function getFactory($class = 'Guzzle\\Service\\Command\\Factory\\MapFactory')
50072 {
50073 return $mock = $this->getMockBuilder($class)
50074 ->disableOriginalConstructor()
50075 ->getMock();
50076 }
50077
50078 public function testIsIterable()
50079 {
50080 $factory = new CompositeFactory(array($this->getFactory(), $this->getFactory()));
50081 $this->assertEquals(2, count($factory));
50082 $this->assertEquals(2, count(iterator_to_array($factory->getIterator())));
50083 }
50084
50085 public function testFindsFactories()
50086 {
50087 $f1 = $this->getFactory();
50088 $f2 = $this->getFactory('Guzzle\\Service\\Command\\Factory\\CompositeFactory');
50089 $factory = new CompositeFactory(array($f1, $f2));
50090 $this->assertNull($factory->find('foo'));
50091 $this->assertNull($factory->find($this->getFactory()));
50092 $this->assertSame($f1, $factory->find('Guzzle\\Service\\Command\\Factory\\MapFactory'));
50093 $this->assertSame($f2, $factory->find('Guzzle\\Service\\Command\\Factory\\CompositeFactory'));
50094 $this->assertSame($f1, $factory->find($f1));
50095 $this->assertSame($f2, $factory->find($f2));
50096
50097 $this->assertFalse($factory->has('foo'));
50098 $this->assertTrue($factory->has('Guzzle\\Service\\Command\\Factory\\MapFactory'));
50099 $this->assertTrue($factory->has('Guzzle\\Service\\Command\\Factory\\CompositeFactory'));
50100 }
50101
50102 public function testCreatesCommands()
50103 {
50104 $factory = new CompositeFactory();
50105 $this->assertNull($factory->factory('foo'));
50106
50107 $f1 = $this->getFactory();
50108 $mockCommand1 = $this->getMockForAbstractClass('Guzzle\\Service\\Command\\AbstractCommand');
50109
50110 $f1->expects($this->once())
50111 ->method('factory')
50112 ->with($this->equalTo('foo'))
50113 ->will($this->returnValue($mockCommand1));
50114
50115 $factory = new CompositeFactory(array($f1));
50116 $this->assertSame($mockCommand1, $factory->factory('foo'));
50117 }
50118
50119 public function testAllowsRemovalOfFactories()
50120 {
50121 $f1 = $this->getFactory();
50122 $f2 = $this->getFactory();
50123 $f3 = $this->getFactory('Guzzle\\Service\\Command\\Factory\\CompositeFactory');
50124 $factories = array($f1, $f2, $f3);
50125 $factory = new CompositeFactory($factories);
50126
50127 $factory->remove('foo');
50128 $this->assertEquals($factories, $factory->getIterator()->getArrayCopy());
50129
50130 $factory->remove($f1);
50131 $this->assertEquals(array($f2, $f3), $factory->getIterator()->getArrayCopy());
50132
50133 $factory->remove('Guzzle\\Service\\Command\\Factory\\MapFactory');
50134 $this->assertEquals(array($f3), $factory->getIterator()->getArrayCopy());
50135
50136 $factory->remove('Guzzle\\Service\\Command\\Factory\\CompositeFactory');
50137 $this->assertEquals(array(), $factory->getIterator()->getArrayCopy());
50138
50139 $factory->remove('foo');
50140 $this->assertEquals(array(), $factory->getIterator()->getArrayCopy());
50141 }
50142
50143 public function testAddsFactoriesBeforeAndAtEnd()
50144 {
50145 $f1 = $this->getFactory();
50146 $f2 = $this->getFactory();
50147 $f3 = $this->getFactory('Guzzle\\Service\\Command\\Factory\\CompositeFactory');
50148 $f4 = $this->getFactory();
50149
50150 $factory = new CompositeFactory();
50151
50152 $factory->add($f1);
50153 $this->assertEquals(array($f1), $factory->getIterator()->getArrayCopy());
50154
50155 $factory->add($f2);
50156 $this->assertEquals(array($f1, $f2), $factory->getIterator()->getArrayCopy());
50157
50158 $factory->add($f3, $f2);
50159 $this->assertEquals(array($f1, $f3, $f2), $factory->getIterator()->getArrayCopy());
50160
50161 $factory->add($f4, 'Guzzle\\Service\\Command\\Factory\\CompositeFactory');
50162 $this->assertEquals(array($f1, $f4, $f3, $f2), $factory->getIterator()->getArrayCopy());
50163 }
50164
50165 public function testProvidesDefaultChainForClients()
50166 {
50167 $client = $this->getMock('Guzzle\\Service\\Client');
50168 $chain = CompositeFactory::getDefaultChain($client);
50169 $a = $chain->getIterator()->getArrayCopy();
50170 $this->assertEquals(1, count($a));
50171 $this->assertInstanceOf('Guzzle\\Service\\Command\\Factory\\ConcreteClassFactory', $a[0]);
50172
50173 $description = $this->getMock('Guzzle\\Service\\Description\\ServiceDescription');
50174 $client->expects($this->once())
50175 ->method('getDescription')
50176 ->will($this->returnValue($description));
50177 $chain = CompositeFactory::getDefaultChain($client);
50178 $a = $chain->getIterator()->getArrayCopy();
50179 $this->assertEquals(2, count($a));
50180 $this->assertInstanceOf('Guzzle\\Service\\Command\\Factory\\ServiceDescriptionFactory', $a[0]);
50181 $this->assertInstanceOf('Guzzle\\Service\\Command\\Factory\\ConcreteClassFactory', $a[1]);
50182 }
50183 }
50184 <?php
50185
50186 namespace Guzzle\Tests\Service\Command;
50187
50188 use Guzzle\Http\Message\Response;
50189 use Guzzle\Service\Client;
50190 use Guzzle\Service\Command\DefaultResponseParser;
50191 use Guzzle\Service\Command\OperationCommand;
50192 use Guzzle\Service\Description\Operation;
50193
50194 /**
50195 * @covers Guzzle\Service\Command\DefaultResponseParser
50196 */
50197 class DefaultResponseParserTest extends \Guzzle\Tests\GuzzleTestCase
50198 {
50199 public function testParsesXmlResponses()
50200 {
50201 $op = new OperationCommand(array(), new Operation());
50202 $op->setClient(new Client());
50203 $request = $op->prepare();
50204 $request->setResponse(new Response(200, array(
50205 'Content-Type' => 'application/xml'
50206 ), '<Foo><Baz>Bar</Baz></Foo>'), true);
50207 $this->assertInstanceOf('SimpleXMLElement', $op->execute());
50208 }
50209
50210 public function testParsesJsonResponses()
50211 {
50212 $op = new OperationCommand(array(), new Operation());
50213 $op->setClient(new Client());
50214 $request = $op->prepare();
50215 $request->setResponse(new Response(200, array(
50216 'Content-Type' => 'application/json'
50217 ), '{"Baz":"Bar"}'), true);
50218 $this->assertEquals(array('Baz' => 'Bar'), $op->execute());
50219 }
50220
50221 /**
50222 * @expectedException \Guzzle\Common\Exception\RuntimeException
50223 */
50224 public function testThrowsExceptionWhenParsingJsonFails()
50225 {
50226 $op = new OperationCommand(array(), new Operation());
50227 $op->setClient(new Client());
50228 $request = $op->prepare();
50229 $request->setResponse(new Response(200, array('Content-Type' => 'application/json'), '{"Baz":ddw}'), true);
50230 $op->execute();
50231 }
50232
50233 public function testAddsContentTypeWhenExpectsIsSetOnCommand()
50234 {
50235 $op = new OperationCommand(array(), new Operation());
50236 $op['command.expects'] = 'application/json';
50237 $op->setClient(new Client());
50238 $request = $op->prepare();
50239 $request->setResponse(new Response(200, null, '{"Baz":"Bar"}'), true);
50240 $this->assertEquals(array('Baz' => 'Bar'), $op->execute());
50241 }
50242 }
50243 <?php
50244
50245 namespace Guzzle\Tests\Service\Command;
50246
50247 use Guzzle\Http\Message\Response;
50248 use Guzzle\Service\Client;
50249 use Guzzle\Service\Command\OperationResponseParser;
50250 use Guzzle\Service\Command\OperationCommand;
50251 use Guzzle\Service\Description\Operation;
50252 use Guzzle\Service\Description\ServiceDescription;
50253 use Guzzle\Service\Command\LocationVisitor\Response\StatusCodeVisitor;
50254 use Guzzle\Service\Command\LocationVisitor\Response\ReasonPhraseVisitor;
50255 use Guzzle\Service\Command\LocationVisitor\Response\JsonVisitor;
50256 use Guzzle\Service\Command\LocationVisitor\Response\BodyVisitor;
50257 use Guzzle\Service\Command\LocationVisitor\VisitorFlyweight;
50258
50259 /**
50260 * @covers Guzzle\Service\Command\OperationResponseParser
50261 * @covers Guzzle\Service\Command\CreateResponseClassEvent
50262 */
50263 class OperationResponseParserTest extends \Guzzle\Tests\GuzzleTestCase
50264 {
50265 public function testHasVisitors()
50266 {
50267 $p = new OperationResponseParser(new VisitorFlyweight(array()));
50268 $visitor = new BodyVisitor();
50269 $p->addVisitor('foo', $visitor);
50270 $this->assertSame($visitor, $this->readAttribute($p, 'factory')->getResponseVisitor('foo'));
50271 }
50272
50273 public function testUsesParentParser()
50274 {
50275 $p = new OperationResponseParser(new VisitorFlyweight());
50276 $operation = new Operation();
50277 $operation->setServiceDescription(new ServiceDescription());
50278 $op = new OperationCommand(array(), $operation);
50279 $op->setResponseParser($p)->setClient(new Client());
50280 $op->prepare()->setResponse(new Response(200, array('Content-Type' => 'application/xml'), '<F><B>C</B></F>'), true);
50281 $this->assertInstanceOf('SimpleXMLElement', $op->execute());
50282 }
50283
50284 public function testVisitsLocations()
50285 {
50286 $parser = new OperationResponseParser(new VisitorFlyweight(array()));
50287 $parser->addVisitor('statusCode', new StatusCodeVisitor());
50288 $parser->addVisitor('reasonPhrase', new ReasonPhraseVisitor());
50289 $parser->addVisitor('json', new JsonVisitor());
50290 $op = new OperationCommand(array(), $this->getDescription()->getOperation('test'));
50291 $op->setResponseParser($parser)->setClient(new Client());
50292 $op->prepare()->setResponse(new Response(201), true);
50293 $result = $op->execute();
50294 $this->assertEquals(201, $result['code']);
50295 $this->assertEquals('Created', $result['phrase']);
50296 }
50297
50298 public function testVisitsLocationsForJsonResponse()
50299 {
50300 $parser = OperationResponseParser::getInstance();
50301 $operation = $this->getDescription()->getOperation('test');
50302 $op = new OperationCommand(array(), $operation);
50303 $op->setResponseParser($parser)->setClient(new Client());
50304 $op->prepare()->setResponse(new Response(200, array(
50305 'Content-Type' => 'application/json'
50306 ), '{"baz":"bar","enigma":"123"}'), true);
50307 $result = $op->execute();
50308 $this->assertEquals(array(
50309 'baz' => 'bar',
50310 'enigma' => '123',
50311 'code' => 200,
50312 'phrase' => 'OK'
50313 ), $result->toArray());
50314 }
50315
50316 public function testSkipsUnkownModels()
50317 {
50318 $parser = OperationResponseParser::getInstance();
50319 $operation = $this->getDescription()->getOperation('test');
50320 $operation->setResponseClass('Baz')->setResponseType('model');
50321 $op = new OperationCommand(array(), $operation);
50322 $op->setResponseParser($parser)->setClient(new Client());
50323 $op->prepare()->setResponse(new Response(201), true);
50324 $this->assertInstanceOf('Guzzle\Http\Message\Response', $op->execute());
50325 }
50326
50327 public function testAllowsModelProcessingToBeDisabled()
50328 {
50329 $parser = OperationResponseParser::getInstance();
50330 $operation = $this->getDescription()->getOperation('test');
50331 $op = new OperationCommand(array('command.response_processing' => 'native'), $operation);
50332 $op->setResponseParser($parser)->setClient(new Client());
50333 $op->prepare()->setResponse(new Response(200, array(
50334 'Content-Type' => 'application/json'
50335 ), '{"baz":"bar","enigma":"123"}'), true);
50336 $result = $op->execute();
50337 $this->assertInstanceOf('Guzzle\Service\Resource\Model', $result);
50338 $this->assertEquals(array(
50339 'baz' => 'bar',
50340 'enigma' => '123'
50341 ), $result->toArray());
50342 }
50343
50344 public function testCanInjectModelSchemaIntoModels()
50345 {
50346 $parser = new OperationResponseParser(VisitorFlyweight::getInstance(), true);
50347 $desc = $this->getDescription();
50348 $operation = $desc->getOperation('test');
50349 $op = new OperationCommand(array(), $operation);
50350 $op->setResponseParser($parser)->setClient(new Client());
50351 $op->prepare()->setResponse(new Response(200, array(
50352 'Content-Type' => 'application/json'
50353 ), '{"baz":"bar","enigma":"123"}'), true);
50354 $result = $op->execute();
50355 $this->assertSame($result->getStructure(), $desc->getModel('Foo'));
50356 }
50357
50358 public function testDoesNotParseXmlWhenNotUsingXmlVisitor()
50359 {
50360 $parser = OperationResponseParser::getInstance();
50361 $description = ServiceDescription::factory(array(
50362 'operations' => array('test' => array('responseClass' => 'Foo')),
50363 'models' => array(
50364 'Foo' => array(
50365 'type' => 'object',
50366 'properties' => array('baz' => array('location' => 'body'))
50367 )
50368 )
50369 ));
50370 $operation = $description->getOperation('test');
50371 $op = new OperationCommand(array(), $operation);
50372 $op->setResponseParser($parser)->setClient(new Client());
50373 $brokenXml = '<broken><><><<xml>>>>>';
50374 $op->prepare()->setResponse(new Response(200, array(
50375 'Content-Type' => 'application/xml'
50376 ), $brokenXml), true);
50377 $result = $op->execute();
50378 $this->assertEquals(array('baz'), $result->getKeys());
50379 $this->assertEquals($brokenXml, (string) $result['baz']);
50380 }
50381
50382 public function testVisitsAdditionalProperties()
50383 {
50384 $parser = OperationResponseParser::getInstance();
50385 $description = ServiceDescription::factory(array(
50386 'operations' => array('test' => array('responseClass' => 'Foo')),
50387 'models' => array(
50388 'Foo' => array(
50389 'type' => 'object',
50390 'properties' => array(
50391 'code' => array('location' => 'statusCode')
50392 ),
50393 'additionalProperties' => array(
50394 'location' => 'json',
50395 'type' => 'object',
50396 'properties' => array(
50397 'a' => array(
50398 'type' => 'string',
50399 'filters' => 'strtoupper'
50400 )
50401 )
50402 )
50403 )
50404 )
50405 ));
50406
50407 $operation = $description->getOperation('test');
50408 $op = new OperationCommand(array(), $operation);
50409 $op->setResponseParser($parser)->setClient(new Client());
50410 $json = '[{"a":"test"},{"a":"baz"}]';
50411 $op->prepare()->setResponse(new Response(200, array('Content-Type' => 'application/json'), $json), true);
50412 $result = $op->execute()->toArray();
50413 $this->assertEquals(array(
50414 'code' => 200,
50415 array('a' => 'TEST'),
50416 array('a' => 'BAZ')
50417 ), $result);
50418 }
50419
50420 /**
50421 * @group issue-399
50422 * @link https://github.com/guzzle/guzzle/issues/399
50423 */
50424 public function testAdditionalPropertiesDisabledDiscardsData()
50425 {
50426 $parser = OperationResponseParser::getInstance();
50427 $description = ServiceDescription::factory(array(
50428 'operations' => array('test' => array('responseClass' => 'Foo')),
50429 'models' => array(
50430 'Foo' => array(
50431 'type' => 'object',
50432 'additionalProperties' => false,
50433 'properties' => array(
50434 'name' => array(
50435 'location' => 'json',
50436 'type' => 'string',
50437 ),
50438 'nested' => array(
50439 'location' => 'json',
50440 'type' => 'object',
50441 'additionalProperties' => false,
50442 'properties' => array(
50443 'width' => array(
50444 'type' => 'integer'
50445 )
50446 ),
50447 ),
50448 'code' => array('location' => 'statusCode')
50449 ),
50450
50451 )
50452 )
50453 ));
50454
50455 $operation = $description->getOperation('test');
50456 $op = new OperationCommand(array(), $operation);
50457 $op->setResponseParser($parser)->setClient(new Client());
50458 $json = '{"name":"test", "volume":2.0, "nested":{"width":10,"bogus":1}}';
50459 $op->prepare()->setResponse(new Response(200, array('Content-Type' => 'application/json'), $json), true);
50460 $result = $op->execute()->toArray();
50461 $this->assertEquals(array(
50462 'name' => 'test',
50463 'nested' => array(
50464 'width' => 10,
50465 ),
50466 'code' => 200
50467 ), $result);
50468 }
50469
50470 public function testCreatesCustomResponseClassInterface()
50471 {
50472 $parser = OperationResponseParser::getInstance();
50473 $description = ServiceDescription::factory(array(
50474 'operations' => array('test' => array('responseClass' => 'Guzzle\Tests\Mock\CustomResponseModel'))
50475 ));
50476 $operation = $description->getOperation('test');
50477 $op = new OperationCommand(array(), $operation);
50478 $op->setResponseParser($parser)->setClient(new Client());
50479 $op->prepare()->setResponse(new Response(200, array('Content-Type' => 'application/json'), 'hi!'), true);
50480 $result = $op->execute();
50481 $this->assertInstanceOf('Guzzle\Tests\Mock\CustomResponseModel', $result);
50482 $this->assertSame($op, $result->command);
50483 }
50484
50485 /**
50486 * @expectedException \Guzzle\Service\Exception\ResponseClassException
50487 * @expectedExceptionMessage must exist
50488 */
50489 public function testEnsuresResponseClassExists()
50490 {
50491 $parser = OperationResponseParser::getInstance();
50492 $description = ServiceDescription::factory(array(
50493 'operations' => array('test' => array('responseClass' => 'Foo\Baz\Bar'))
50494 ));
50495 $operation = $description->getOperation('test');
50496 $op = new OperationCommand(array(), $operation);
50497 $op->setResponseParser($parser)->setClient(new Client());
50498 $op->prepare()->setResponse(new Response(200, array('Content-Type' => 'application/json'), 'hi!'), true);
50499 $op->execute();
50500 }
50501
50502 /**
50503 * @expectedException \Guzzle\Service\Exception\ResponseClassException
50504 * @expectedExceptionMessage and implement
50505 */
50506 public function testEnsuresResponseClassImplementsResponseClassInterface()
50507 {
50508 $parser = OperationResponseParser::getInstance();
50509 $description = ServiceDescription::factory(array(
50510 'operations' => array('test' => array('responseClass' => __CLASS__))
50511 ));
50512 $operation = $description->getOperation('test');
50513 $op = new OperationCommand(array(), $operation);
50514 $op->setResponseParser($parser)->setClient(new Client());
50515 $op->prepare()->setResponse(new Response(200, array('Content-Type' => 'application/json'), 'hi!'), true);
50516 $op->execute();
50517 }
50518
50519 protected function getDescription()
50520 {
50521 return ServiceDescription::factory(array(
50522 'operations' => array('test' => array('responseClass' => 'Foo')),
50523 'models' => array(
50524 'Foo' => array(
50525 'type' => 'object',
50526 'properties' => array(
50527 'baz' => array('type' => 'string', 'location' => 'json'),
50528 'code' => array('location' => 'statusCode'),
50529 'phrase' => array('location' => 'reasonPhrase'),
50530 )
50531 )
50532 )
50533 ));
50534 }
50535
50536 public function testCanAddListenerToParseDomainObjects()
50537 {
50538 $client = new Client();
50539 $client->setDescription(ServiceDescription::factory(array(
50540 'operations' => array('test' => array('responseClass' => 'FooBazBar'))
50541 )));
50542 $foo = new \stdClass();
50543 $client->getEventDispatcher()->addListener('command.parse_response', function ($e) use ($foo) {
50544 $e['result'] = $foo;
50545 });
50546 $command = $client->getCommand('test');
50547 $command->prepare()->setResponse(new Response(200), true);
50548 $result = $command->execute();
50549 $this->assertSame($result, $foo);
50550 }
50551 }
50552 <?php
50553
50554 namespace Guzzle\Tests\Service\Command;
50555
50556 use Guzzle\Plugin\Mock\MockPlugin;
50557 use Guzzle\Http\EntityBody;
50558 use Guzzle\Http\Message\Response;
50559 use Guzzle\Service\Client;
50560 use Guzzle\Service\Command\AbstractCommand;
50561 use Guzzle\Service\Description\Operation;
50562 use Guzzle\Service\Description\Parameter;
50563 use Guzzle\Service\Description\SchemaValidator;
50564 use Guzzle\Service\Description\ServiceDescription;
50565 use Guzzle\Tests\Service\Mock\Command\MockCommand;
50566 use Guzzle\Tests\Service\Mock\Command\Sub\Sub;
50567
50568 /**
50569 * @covers Guzzle\Service\Command\AbstractCommand
50570 */
50571 class CommandTest extends AbstractCommandTest
50572 {
50573 public function testConstructorAddsDefaultParams()
50574 {
50575 $command = new MockCommand();
50576 $this->assertEquals('123', $command->get('test'));
50577 $this->assertFalse($command->isPrepared());
50578 $this->assertFalse($command->isExecuted());
50579 }
50580
50581 public function testDeterminesShortName()
50582 {
50583 $api = new Operation(array('name' => 'foobar'));
50584 $command = new MockCommand(array(), $api);
50585 $this->assertEquals('foobar', $command->getName());
50586
50587 $command = new MockCommand();
50588 $this->assertEquals('mock_command', $command->getName());
50589
50590 $command = new Sub();
50591 $this->assertEquals('sub.sub', $command->getName());
50592 }
50593
50594 /**
50595 * @expectedException RuntimeException
50596 */
50597 public function testGetRequestThrowsExceptionBeforePreparation()
50598 {
50599 $command = new MockCommand();
50600 $command->getRequest();
50601 }
50602
50603 public function testGetResponseExecutesCommandsWhenNeeded()
50604 {
50605 $response = new Response(200);
50606 $client = $this->getClient();
50607 $this->setMockResponse($client, array($response));
50608 $command = new MockCommand();
50609 $command->setClient($client);
50610 $this->assertSame($response, $command->getResponse());
50611 $this->assertSame($response, $command->getResponse());
50612 }
50613
50614 public function testGetResultExecutesCommandsWhenNeeded()
50615 {
50616 $response = new Response(200);
50617 $client = $this->getClient();
50618 $this->setMockResponse($client, array($response));
50619 $command = new MockCommand();
50620 $command->setClient($client);
50621 $this->assertSame($response, $command->getResult());
50622 $this->assertSame($response, $command->getResult());
50623 }
50624
50625 public function testSetClient()
50626 {
50627 $command = new MockCommand();
50628 $client = $this->getClient();
50629
50630 $command->setClient($client);
50631 $this->assertEquals($client, $command->getClient());
50632
50633 unset($client);
50634 unset($command);
50635
50636 $command = new MockCommand();
50637 $client = $this->getClient();
50638
50639 $command->setClient($client)->prepare();
50640 $this->assertEquals($client, $command->getClient());
50641 $this->assertTrue($command->isPrepared());
50642 }
50643
50644 public function testExecute()
50645 {
50646 $client = $this->getClient();
50647 $response = new Response(200, array(
50648 'Content-Type' => 'application/xml'
50649 ), '<xml><data>123</data></xml>');
50650 $this->setMockResponse($client, array($response));
50651 $command = new MockCommand();
50652 $this->assertSame($command, $command->setClient($client));
50653
50654 // Returns the result of the command
50655 $this->assertInstanceOf('SimpleXMLElement', $command->execute());
50656
50657 $this->assertTrue($command->isPrepared());
50658 $this->assertTrue($command->isExecuted());
50659 $this->assertSame($response, $command->getResponse());
50660 $this->assertInstanceOf('Guzzle\\Http\\Message\\Request', $command->getRequest());
50661 // Make sure that the result was automatically set to a SimpleXMLElement
50662 $this->assertInstanceOf('SimpleXMLElement', $command->getResult());
50663 $this->assertEquals('123', (string) $command->getResult()->data);
50664 }
50665
50666 public function testConvertsJsonResponsesToArray()
50667 {
50668 $client = $this->getClient();
50669 $this->setMockResponse($client, array(
50670 new \Guzzle\Http\Message\Response(200, array(
50671 'Content-Type' => 'application/json'
50672 ), '{ "key": "Hi!" }'
50673 )
50674 ));
50675 $command = new MockCommand();
50676 $command->setClient($client);
50677 $command->execute();
50678 $this->assertEquals(array(
50679 'key' => 'Hi!'
50680 ), $command->getResult());
50681 }
50682
50683 /**
50684 * @expectedException \Guzzle\Common\Exception\RuntimeException
50685 */
50686 public function testConvertsInvalidJsonResponsesToArray()
50687 {
50688 $client = $this->getClient();
50689 $this->setMockResponse($client, array(
50690 new \Guzzle\Http\Message\Response(200, array(
50691 'Content-Type' => 'application/json'
50692 ), '{ "key": "Hi!" }invalid'
50693 )
50694 ));
50695 $command = new MockCommand();
50696 $command->setClient($client);
50697 $command->execute();
50698 }
50699
50700 public function testProcessResponseIsNotXml()
50701 {
50702 $client = $this->getClient();
50703 $this->setMockResponse($client, array(
50704 new Response(200, array(
50705 'Content-Type' => 'application/octet-stream'
50706 ), 'abc,def,ghi')
50707 ));
50708 $command = new MockCommand();
50709 $client->execute($command);
50710
50711 // Make sure that the result was not converted to XML
50712 $this->assertFalse($command->getResult() instanceof \SimpleXMLElement);
50713 }
50714
50715 /**
50716 * @expectedException RuntimeException
50717 */
50718 public function testExecuteThrowsExceptionWhenNoClientIsSet()
50719 {
50720 $command = new MockCommand();
50721 $command->execute();
50722 }
50723
50724 /**
50725 * @expectedException RuntimeException
50726 */
50727 public function testPrepareThrowsExceptionWhenNoClientIsSet()
50728 {
50729 $command = new MockCommand();
50730 $command->prepare();
50731 }
50732
50733 public function testCommandsAllowsCustomRequestHeaders()
50734 {
50735 $command = new MockCommand();
50736 $command->getRequestHeaders()->set('test', '123');
50737 $this->assertInstanceOf('Guzzle\Common\Collection', $command->getRequestHeaders());
50738 $this->assertEquals('123', $command->getRequestHeaders()->get('test'));
50739
50740 $command->setClient($this->getClient())->prepare();
50741 $this->assertEquals('123', (string) $command->getRequest()->getHeader('test'));
50742 }
50743
50744 public function testCommandsAllowsCustomRequestHeadersAsArray()
50745 {
50746 $command = new MockCommand(array(AbstractCommand::HEADERS_OPTION => array('Foo' => 'Bar')));
50747 $this->assertInstanceOf('Guzzle\Common\Collection', $command->getRequestHeaders());
50748 $this->assertEquals('Bar', $command->getRequestHeaders()->get('Foo'));
50749 }
50750
50751 private function getOperation()
50752 {
50753 return new Operation(array(
50754 'name' => 'foobar',
50755 'httpMethod' => 'POST',
50756 'class' => 'Guzzle\\Tests\\Service\\Mock\\Command\\MockCommand',
50757 'parameters' => array(
50758 'test' => array(
50759 'default' => '123',
50760 'type' => 'string'
50761 )
50762 )));
50763 }
50764
50765 public function testCommandsUsesOperation()
50766 {
50767 $api = $this->getOperation();
50768 $command = new MockCommand(array(), $api);
50769 $this->assertSame($api, $command->getOperation());
50770 $command->setClient($this->getClient())->prepare();
50771 $this->assertEquals('123', $command->get('test'));
50772 $this->assertSame($api, $command->getOperation($api));
50773 }
50774
50775 public function testCloneMakesNewRequest()
50776 {
50777 $client = $this->getClient();
50778 $command = new MockCommand(array(), $this->getOperation());
50779 $command->setClient($client);
50780
50781 $command->prepare();
50782 $this->assertTrue($command->isPrepared());
50783
50784 $command2 = clone $command;
50785 $this->assertFalse($command2->isPrepared());
50786 }
50787
50788 public function testHasOnCompleteMethod()
50789 {
50790 $that = $this;
50791 $called = 0;
50792
50793 $testFunction = function($command) use (&$called, $that) {
50794 $called++;
50795 $that->assertInstanceOf('Guzzle\Service\Command\CommandInterface', $command);
50796 };
50797
50798 $client = $this->getClient();
50799 $command = new MockCommand(array(
50800 'command.on_complete' => $testFunction
50801 ), $this->getOperation());
50802 $command->setClient($client);
50803
50804 $command->prepare()->setResponse(new Response(200), true);
50805 $command->execute();
50806 $this->assertEquals(1, $called);
50807 }
50808
50809 /**
50810 * @expectedException \Guzzle\Common\Exception\InvalidArgumentException
50811 */
50812 public function testOnCompleteMustBeCallable()
50813 {
50814 $client = $this->getClient();
50815 $command = new MockCommand();
50816 $command->setOnComplete('foo');
50817 }
50818
50819 public function testCanSetResultManually()
50820 {
50821 $client = $this->getClient();
50822 $client->getEventDispatcher()->addSubscriber(new MockPlugin(array(
50823 new Response(200)
50824 )));
50825 $command = new MockCommand();
50826 $client->execute($command);
50827 $command->setResult('foo!');
50828 $this->assertEquals('foo!', $command->getResult());
50829 }
50830
50831 public function testCanInitConfig()
50832 {
50833 $command = $this->getMockBuilder('Guzzle\\Service\\Command\\AbstractCommand')
50834 ->setConstructorArgs(array(array(
50835 'foo' => 'bar'
50836 ), new Operation(array(
50837 'parameters' => array(
50838 'baz' => new Parameter(array(
50839 'default' => 'baaar'
50840 ))
50841 )
50842 ))))
50843 ->getMockForAbstractClass();
50844
50845 $this->assertEquals('bar', $command['foo']);
50846 $this->assertEquals('baaar', $command['baz']);
50847 }
50848
50849 public function testAddsCurlOptionsToRequestsWhenPreparing()
50850 {
50851 $command = new MockCommand(array(
50852 'foo' => 'bar',
50853 'curl.options' => array('CURLOPT_PROXYPORT' => 8080)
50854 ));
50855 $client = new Client();
50856 $command->setClient($client);
50857 $request = $command->prepare();
50858 $this->assertEquals(8080, $request->getCurlOptions()->get(CURLOPT_PROXYPORT));
50859 }
50860
50861 public function testIsInvokable()
50862 {
50863 $client = $this->getClient();
50864 $response = new Response(200);
50865 $this->setMockResponse($client, array($response));
50866 $command = new MockCommand();
50867 $command->setClient($client);
50868 // Returns the result of the command
50869 $this->assertSame($response, $command());
50870 }
50871
50872 public function testCreatesDefaultOperation()
50873 {
50874 $command = $this->getMockBuilder('Guzzle\Service\Command\AbstractCommand')->getMockForAbstractClass();
50875 $this->assertInstanceOf('Guzzle\Service\Description\Operation', $command->getOperation());
50876 }
50877
50878 public function testAllowsValidatorToBeInjected()
50879 {
50880 $command = $this->getMockBuilder('Guzzle\Service\Command\AbstractCommand')->getMockForAbstractClass();
50881 $v = new SchemaValidator();
50882 $command->setValidator($v);
50883 $this->assertSame($v, $this->readAttribute($command, 'validator'));
50884 }
50885
50886 public function testCanDisableValidation()
50887 {
50888 $command = new MockCommand();
50889 $command->setClient(new \Guzzle\Service\Client());
50890 $v = $this->getMockBuilder('Guzzle\Service\Description\SchemaValidator')
50891 ->setMethods(array('validate'))
50892 ->getMock();
50893 $v->expects($this->never())->method('validate');
50894 $command->setValidator($v);
50895 $command->set(AbstractCommand::DISABLE_VALIDATION, true);
50896 $command->prepare();
50897 }
50898
50899 public function testValidatorDoesNotUpdateNonDefaultValues()
50900 {
50901 $command = new MockCommand(array('test' => 123, 'foo' => 'bar'));
50902 $command->setClient(new \Guzzle\Service\Client());
50903 $command->prepare();
50904 $this->assertEquals(123, $command->get('test'));
50905 $this->assertEquals('bar', $command->get('foo'));
50906 }
50907
50908 public function testValidatorUpdatesDefaultValues()
50909 {
50910 $command = new MockCommand();
50911 $command->setClient(new \Guzzle\Service\Client());
50912 $command->prepare();
50913 $this->assertEquals(123, $command->get('test'));
50914 $this->assertEquals('abc', $command->get('_internal'));
50915 }
50916
50917 /**
50918 * @expectedException \Guzzle\Service\Exception\ValidationException
50919 * @expectedExceptionMessage [Foo] Baz
50920 */
50921 public function testValidatesCommandBeforeSending()
50922 {
50923 $command = new MockCommand();
50924 $command->setClient(new \Guzzle\Service\Client());
50925 $v = $this->getMockBuilder('Guzzle\Service\Description\SchemaValidator')
50926 ->setMethods(array('validate', 'getErrors'))
50927 ->getMock();
50928 $v->expects($this->any())->method('validate')->will($this->returnValue(false));
50929 $v->expects($this->any())->method('getErrors')->will($this->returnValue(array('[Foo] Baz', '[Bar] Boo')));
50930 $command->setValidator($v);
50931 $command->prepare();
50932 }
50933
50934 /**
50935 * @expectedException \Guzzle\Service\Exception\ValidationException
50936 * @expectedExceptionMessage Validation errors: [abc] must be of type string
50937 */
50938 public function testValidatesAdditionalParameters()
50939 {
50940 $description = ServiceDescription::factory(array(
50941 'operations' => array(
50942 'foo' => array(
50943 'parameters' => array(
50944 'baz' => array('type' => 'integer')
50945 ),
50946 'additionalParameters' => array(
50947 'type' => 'string'
50948 )
50949 )
50950 )
50951 ));
50952
50953 $client = new Client();
50954 $client->setDescription($description);
50955 $command = $client->getCommand('foo', array(
50956 'abc' => false,
50957 'command.headers' => array('foo' => 'bar')
50958 ));
50959 $command->prepare();
50960 }
50961
50962 public function testCanChangeResponseBody()
50963 {
50964 $body = EntityBody::factory();
50965 $command = new MockCommand();
50966 $command->setClient(new \Guzzle\Service\Client());
50967 $command->set(AbstractCommand::RESPONSE_BODY, $body);
50968 $request = $command->prepare();
50969 $this->assertSame($body, $this->readAttribute($request, 'responseBody'));
50970 }
50971 }
50972 <?php
50973
50974 namespace Guzzle\Tests\Service\Command;
50975
50976 use Guzzle\Http\Message\RequestFactory;
50977 use Guzzle\Service\Command\ClosureCommand;
50978 use Guzzle\Service\Client;
50979
50980 /**
50981 * @covers Guzzle\Service\Command\ClosureCommand
50982 */
50983 class ClosureCommandTest extends \Guzzle\Tests\GuzzleTestCase
50984 {
50985 /**
50986 * @expectedException InvalidArgumentException
50987 * @expectedExceptionMessage A closure must be passed in the parameters array
50988 */
50989 public function testConstructorValidatesClosure()
50990 {
50991 $c = new ClosureCommand();
50992 }
50993
50994 public function testExecutesClosure()
50995 {
50996 $c = new ClosureCommand(array(
50997 'closure' => function($command, $api) {
50998 $command->set('testing', '123');
50999 $request = RequestFactory::getInstance()->create('GET', 'http://www.test.com/');
51000 return $request;
51001 }
51002 ));
51003
51004 $client = $this->getServiceBuilder()->get('mock');
51005 $c->setClient($client)->prepare();
51006 $this->assertEquals('123', $c->get('testing'));
51007 $this->assertEquals('http://www.test.com/', $c->getRequest()->getUrl());
51008 }
51009
51010 /**
51011 * @expectedException UnexpectedValueException
51012 * @expectedExceptionMessage Closure command did not return a RequestInterface object
51013 */
51014 public function testMustReturnRequest()
51015 {
51016 $c = new ClosureCommand(array(
51017 'closure' => function($command, $api) {
51018 return false;
51019 }
51020 ));
51021
51022 $client = $this->getServiceBuilder()->get('mock');
51023 $c->setClient($client)->prepare();
51024 }
51025 }
51026 <?php
51027
51028 namespace Guzzle\Tests\Service\Command;
51029
51030 use Guzzle\Http\Message\EntityEnclosingRequest;
51031 use Guzzle\Http\Message\Response;
51032 use Guzzle\Service\Client;
51033 use Guzzle\Service\Command\OperationCommand;
51034 use Guzzle\Service\Description\Operation;
51035 use Guzzle\Service\Description\ServiceDescription;
51036 use Guzzle\Service\Command\DefaultRequestSerializer;
51037 use Guzzle\Service\Resource\Model;
51038 use Guzzle\Service\Command\LocationVisitor\VisitorFlyweight;
51039
51040 /**
51041 * @covers Guzzle\Service\Command\OperationCommand
51042 */
51043 class OperationCommandTest extends \Guzzle\Tests\GuzzleTestCase
51044 {
51045 public function testHasRequestSerializer()
51046 {
51047 $operation = new OperationCommand();
51048 $a = $operation->getRequestSerializer();
51049 $b = new DefaultRequestSerializer(VisitorFlyweight::getInstance());
51050 $operation->setRequestSerializer($b);
51051 $this->assertNotSame($a, $operation->getRequestSerializer());
51052 }
51053
51054 public function testPreparesRequestUsingSerializer()
51055 {
51056 $op = new OperationCommand(array(), new Operation());
51057 $op->setClient(new Client());
51058 $s = $this->getMockBuilder('Guzzle\Service\Command\RequestSerializerInterface')
51059 ->setMethods(array('prepare'))
51060 ->getMockForAbstractClass();
51061 $s->expects($this->once())
51062 ->method('prepare')
51063 ->will($this->returnValue(new EntityEnclosingRequest('POST', 'http://foo.com')));
51064 $op->setRequestSerializer($s);
51065 $op->prepare();
51066 }
51067
51068 public function testParsesResponsesWithResponseParser()
51069 {
51070 $op = new OperationCommand(array(), new Operation());
51071 $p = $this->getMockBuilder('Guzzle\Service\Command\ResponseParserInterface')
51072 ->setMethods(array('parse'))
51073 ->getMockForAbstractClass();
51074 $p->expects($this->once())
51075 ->method('parse')
51076 ->will($this->returnValue(array('foo' => 'bar')));
51077 $op->setResponseParser($p);
51078 $op->setClient(new Client());
51079 $request = $op->prepare();
51080 $request->setResponse(new Response(200), true);
51081 $this->assertEquals(array('foo' => 'bar'), $op->execute());
51082 }
51083
51084 public function testParsesResponsesUsingModelParserWhenMatchingModelIsFound()
51085 {
51086 $description = ServiceDescription::factory(array(
51087 'operations' => array(
51088 'foo' => array('responseClass' => 'bar', 'responseType' => 'model')
51089 ),
51090 'models' => array(
51091 'bar' => array(
51092 'type' => 'object',
51093 'properties' => array(
51094 'Baz' => array('type' => 'string', 'location' => 'xml')
51095 )
51096 )
51097 )
51098 ));
51099
51100 $op = new OperationCommand(array(), $description->getOperation('foo'));
51101 $op->setClient(new Client());
51102 $request = $op->prepare();
51103 $request->setResponse(new Response(200, array(
51104 'Content-Type' => 'application/xml'
51105 ), '<Foo><Baz>Bar</Baz></Foo>'), true);
51106 $result = $op->execute();
51107 $this->assertEquals(new Model(array('Baz' => 'Bar')), $result);
51108 }
51109
51110 public function testAllowsRawResponses()
51111 {
51112 $description = new ServiceDescription(array(
51113 'operations' => array('foo' => array('responseClass' => 'bar', 'responseType' => 'model')),
51114 'models' => array('bar' => array())
51115 ));
51116 $op = new OperationCommand(array(
51117 OperationCommand::RESPONSE_PROCESSING => OperationCommand::TYPE_RAW
51118 ), $description->getOperation('foo'));
51119 $op->setClient(new Client());
51120 $request = $op->prepare();
51121 $response = new Response(200, array(
51122 'Content-Type' => 'application/xml'
51123 ), '<Foo><Baz>Bar</Baz></Foo>');
51124 $request->setResponse($response, true);
51125 $this->assertSame($response, $op->execute());
51126 }
51127 }
51128 <?php
51129
51130 namespace Guzzle\Tests\Service\Command;
51131
51132 use Guzzle\Service\Client;
51133 use Guzzle\Service\Description\ServiceDescription;
51134
51135 abstract class AbstractCommandTest extends \Guzzle\Tests\GuzzleTestCase
51136 {
51137 protected function getClient()
51138 {
51139 $client = new Client('http://www.google.com/');
51140
51141 return $client->setDescription(ServiceDescription::factory(__DIR__ . '/../../TestData/test_service.json'));
51142 }
51143 }
51144 <?php
51145
51146 namespace Guzzle\Tests\Service\Command\LocationVisitor\Request;
51147
51148 use Guzzle\Service\Description\Parameter;
51149 use Guzzle\Service\Command\LocationVisitor\Request\HeaderVisitor as Visitor;
51150
51151 /**
51152 * @covers Guzzle\Service\Command\LocationVisitor\Request\HeaderVisitor
51153 */
51154 class HeaderVisitorTest extends AbstractVisitorTestCase
51155 {
51156 /**
51157 * @expectedException \Guzzle\Common\Exception\InvalidArgumentException
51158 */
51159 public function testValidatesHeaderMapsAreArrays()
51160 {
51161 $visitor = new Visitor();
51162 $param = $this->getNestedCommand('header')->getParam('foo')->setSentAs('test');
51163 $param->setAdditionalProperties(new Parameter(array()));
51164 $visitor->visit($this->command, $this->request, $param, 'test');
51165 }
51166
51167 public function testVisitsLocation()
51168 {
51169 $visitor = new Visitor();
51170 $param = $this->getNestedCommand('header')->getParam('foo')->setSentAs('test');
51171 $param->setAdditionalProperties(false);
51172 $visitor->visit($this->command, $this->request, $param, '123');
51173 $this->assertEquals('123', (string) $this->request->getHeader('test'));
51174 }
51175
51176 public function testVisitsMappedPrefixHeaders()
51177 {
51178 $visitor = new Visitor();
51179 $param = $this->getNestedCommand('header')->getParam('foo')->setSentAs('test');
51180 $param->setSentAs('x-foo-');
51181 $param->setAdditionalProperties(new Parameter(array(
51182 'type' => 'string'
51183 )));
51184 $visitor->visit($this->command, $this->request, $param, array(
51185 'bar' => 'test',
51186 'baz' => '123'
51187 ));
51188 $this->assertEquals('test', (string) $this->request->getHeader('x-foo-bar'));
51189 $this->assertEquals('123', (string) $this->request->getHeader('x-foo-baz'));
51190 }
51191 }
51192 <?php
51193
51194 namespace Guzzle\Tests\Service\Command\LocationVisitor\Request;
51195
51196 use Guzzle\Service\Client;
51197 use Guzzle\Service\Description\ServiceDescription;
51198 use Guzzle\Http\Message\PostFile;
51199 use Guzzle\Service\Command\LocationVisitor\Request\PostFileVisitor as Visitor;
51200
51201 /**
51202 * @covers Guzzle\Service\Command\LocationVisitor\Request\PostFileVisitor
51203 */
51204 class PostFileVisitorTest extends AbstractVisitorTestCase
51205 {
51206 public function testVisitsLocation()
51207 {
51208 $visitor = new Visitor();
51209 $param = $this->getNestedCommand('postFile')->getParam('foo');
51210
51211 // Test using a path to a file
51212 $visitor->visit($this->command, $this->request, $param->setSentAs('test_3'), __FILE__);
51213 $this->assertInternalType('array', $this->request->getPostFile('test_3'));
51214
51215 // Test with a PostFile
51216 $visitor->visit($this->command, $this->request, $param->setSentAs(null), new PostFile('baz', __FILE__));
51217 $this->assertInternalType('array', $this->request->getPostFile('baz'));
51218 }
51219
51220 public function testVisitsLocationWithMultipleFiles()
51221 {
51222 $description = ServiceDescription::factory(array(
51223 'operations' => array(
51224 'DoPost' => array(
51225 'httpMethod' => 'POST',
51226 'parameters' => array(
51227 'foo' => array(
51228 'location' => 'postFile',
51229 'type' => array('string', 'array')
51230 )
51231 )
51232 )
51233 )
51234 ));
51235 $this->getServer()->flush();
51236 $this->getServer()->enqueue(array("HTTP/1.1 200 OK\r\nContent-Length:0\r\n\r\n"));
51237 $client = new Client($this->getServer()->getUrl());
51238 $client->setDescription($description);
51239 $command = $client->getCommand('DoPost', array('foo' => array(__FILE__, __FILE__)));
51240 $command->execute();
51241 $received = $this->getServer()->getReceivedRequests();
51242 $this->assertContains('name="foo[0]";', $received[0]);
51243 $this->assertContains('name="foo[1]";', $received[0]);
51244 }
51245 }
51246 <?php
51247
51248 namespace Guzzle\Tests\Service\Command\LocationVisitor\Request;
51249
51250 use Guzzle\Http\EntityBody;
51251 use Guzzle\Service\Command\LocationVisitor\Request\BodyVisitor as Visitor;
51252
51253 /**
51254 * @covers Guzzle\Service\Command\LocationVisitor\Request\BodyVisitor
51255 */
51256 class BodyVisitorTest extends AbstractVisitorTestCase
51257 {
51258 public function testVisitsLocation()
51259 {
51260 $visitor = new Visitor();
51261 $param = $this->getNestedCommand('body')->getParam('foo')->setSentAs('Foo');
51262 $visitor->visit($this->command, $this->request, $param, '123');
51263 $this->assertEquals('123', (string) $this->request->getBody());
51264 $this->assertNull($this->request->getHeader('Expect'));
51265 }
51266
51267 public function testAddsExpectHeaderWhenSetToTrue()
51268 {
51269 $visitor = new Visitor();
51270 $param = $this->getNestedCommand('body')->getParam('foo')->setSentAs('Foo');
51271 $param->setData('expect_header', true);
51272 $visitor->visit($this->command, $this->request, $param, '123');
51273 $this->assertEquals('123', (string) $this->request->getBody());
51274 }
51275
51276 public function testCanDisableExpectHeader()
51277 {
51278 $visitor = new Visitor();
51279 $param = $this->getNestedCommand('body')->getParam('foo')->setSentAs('Foo');
51280 $param->setData('expect_header', false);
51281 $visitor->visit($this->command, $this->request, $param, '123');
51282 $this->assertNull($this->request->getHeader('Expect'));
51283 }
51284
51285 public function testCanSetExpectHeaderBasedOnSize()
51286 {
51287 $visitor = new Visitor();
51288 $param = $this->getNestedCommand('body')->getParam('foo')->setSentAs('Foo');
51289 // The body is less than the cutoff
51290 $param->setData('expect_header', 5);
51291 $visitor->visit($this->command, $this->request, $param, '123');
51292 $this->assertNull($this->request->getHeader('Expect'));
51293 // Now check when the body is greater than the cutoff
51294 $param->setData('expect_header', 2);
51295 $visitor->visit($this->command, $this->request, $param, '123');
51296 $this->assertEquals('100-Continue', (string) $this->request->getHeader('Expect'));
51297 }
51298
51299 public function testAddsContentEncodingWhenSetOnBody()
51300 {
51301 $visitor = new Visitor();
51302 $param = $this->getNestedCommand('body')->getParam('foo')->setSentAs('Foo');
51303 $body = EntityBody::factory('foo');
51304 $body->compress();
51305 $visitor->visit($this->command, $this->request, $param, $body);
51306 $this->assertEquals('gzip', (string) $this->request->getHeader('Content-Encoding'));
51307 }
51308 }
51309 <?php
51310
51311 namespace Guzzle\Tests\Service\Command\LocationVisitor\Request;
51312
51313 use Guzzle\Service\Command\LocationVisitor\Request\JsonVisitor as Visitor;
51314
51315 /**
51316 * @covers Guzzle\Service\Command\LocationVisitor\Request\JsonVisitor
51317 * @covers Guzzle\Service\Command\LocationVisitor\Request\AbstractRequestVisitor::resolveRecursively
51318 */
51319 class JsonVisitorTest extends AbstractVisitorTestCase
51320 {
51321 public function testVisitsLocation()
51322 {
51323 $visitor = new Visitor();
51324 // Test after when no body query values were found
51325 $visitor->after($this->command, $this->request);
51326
51327 $param = $this->getNestedCommand('json')->getParam('foo');
51328 $visitor->visit($this->command, $this->request, $param->setSentAs('test'), '123');
51329 $visitor->visit($this->command, $this->request, $param->setSentAs('test2'), 'abc');
51330 $visitor->after($this->command, $this->request);
51331 $this->assertEquals('{"test":"123","test2":"abc"}', (string) $this->request->getBody());
51332 }
51333
51334 public function testAddsJsonHeader()
51335 {
51336 $visitor = new Visitor();
51337 $visitor->setContentTypeHeader('application/json-foo');
51338 $param = $this->getNestedCommand('json')->getParam('foo');
51339 $visitor->visit($this->command, $this->request, $param->setSentAs('test'), '123');
51340 $visitor->after($this->command, $this->request);
51341 $this->assertEquals('application/json-foo', (string) $this->request->getHeader('Content-Type'));
51342 }
51343
51344 public function testRecursivelyBuildsJsonBodies()
51345 {
51346 $command = $this->getCommand('json');
51347 $request = $command->prepare();
51348 $this->assertEquals('{"Foo":{"test":{"baz":true,"Jenga_Yall!":"HELLO"},"bar":123}}', (string) $request->getBody());
51349 }
51350
51351 public function testAppliesFiltersToAdditionalProperties()
51352 {
51353 $command = $this->getCommand('json');
51354 $command->set('foo', array('not_set' => 'abc'));
51355 $request = $command->prepare();
51356 $result = json_decode($request->getBody(), true);
51357 $this->assertEquals('ABC', $result['Foo']['not_set']);
51358 }
51359
51360 public function testAppliesFiltersToArrayItemValues()
51361 {
51362 $command = $this->getCommand('json');
51363 $command->set('arr', array('a', 'b'));
51364 $request = $command->prepare();
51365 $result = json_decode($request->getBody(), true);
51366 $this->assertEquals(array('A', 'B'), $result['arr']);
51367 }
51368 }
51369 <?php
51370
51371 namespace Guzzle\Tests\Service\Command\LocationVisitor\Request;
51372
51373 use Guzzle\Service\Command\LocationVisitor\Request\QueryVisitor as Visitor;
51374
51375 /**
51376 * @covers Guzzle\Service\Command\LocationVisitor\Request\QueryVisitor
51377 */
51378 class QueryVisitorTest extends AbstractVisitorTestCase
51379 {
51380 public function testVisitsLocation()
51381 {
51382 $visitor = new Visitor();
51383 $param = $this->getNestedCommand('query')->getParam('foo')->setSentAs('test');
51384 $visitor->visit($this->command, $this->request, $param, '123');
51385 $this->assertEquals('123', $this->request->getQuery()->get('test'));
51386 }
51387
51388 /**
51389 * @covers Guzzle\Service\Command\LocationVisitor\Request\QueryVisitor
51390 * @covers Guzzle\Service\Command\LocationVisitor\Request\AbstractRequestVisitor::resolveRecursively
51391 */
51392 public function testRecursivelyBuildsQueryStrings()
51393 {
51394 $command = $this->getCommand('query');
51395 $command->getOperation()->getParam('foo')->setSentAs('Foo');
51396 $request = $command->prepare();
51397 $this->assertEquals(
51398 'Foo[test][baz]=1&Foo[test][Jenga_Yall!]=HELLO&Foo[bar]=123',
51399 rawurldecode($request->getQuery())
51400 );
51401 }
51402
51403 /**
51404 * @covers Guzzle\Service\Command\LocationVisitor\Request\AbstractRequestVisitor::resolveRecursively
51405 */
51406 public function testFiltersAreAppliedToArrayParamType()
51407 {
51408 $command = $this->getCommandWithArrayParamAndFilters();
51409 $request = $command->prepare();
51410 $query = $request->getQuery();
51411 // param type 'string'
51412 $this->assertEquals('BAR', $query->get('Foo'));
51413 // param type 'array'
51414 $this->assertEquals('123,456,789', $query->get('Arr'));
51415 }
51416 }
51417 <?php
51418
51419 namespace Guzzle\Tests\Service\Command\LocationVisitor\Request;
51420
51421 use Guzzle\Service\Command\LocationVisitor\Request\XmlVisitor;
51422 use Guzzle\Service\Client;
51423 use Guzzle\Service\Description\Parameter;
51424 use Guzzle\Service\Description\Operation;
51425 use Guzzle\Http\Message\EntityEnclosingRequest;
51426
51427 /**
51428 * @covers Guzzle\Service\Command\LocationVisitor\Request\XmlVisitor
51429 */
51430 class XmlVisitorTest extends AbstractVisitorTestCase
51431 {
51432 public function xmlProvider()
51433 {
51434 return array(
51435 array(
51436 array(
51437 'data' => array(
51438 'xmlRoot' => array(
51439 'name' => 'test',
51440 'namespaces' => 'http://foo.com'
51441 )
51442 ),
51443 'parameters' => array(
51444 'Foo' => array('location' => 'xml', 'type' => 'string'),
51445 'Baz' => array('location' => 'xml', 'type' => 'string')
51446 )
51447 ),
51448 array('Foo' => 'test', 'Baz' => 'bar'),
51449 '<test xmlns="http://foo.com"><Foo>test</Foo><Baz>bar</Baz></test>'
51450 ),
51451 // Ensure that the content-type is not added
51452 array(array('parameters' => array('Foo' => array('location' => 'xml', 'type' => 'string'))), array(), ''),
51453 // Test with adding attributes and no namespace
51454 array(
51455 array(
51456 'data' => array(
51457 'xmlRoot' => array(
51458 'name' => 'test'
51459 )
51460 ),
51461 'parameters' => array(
51462 'Foo' => array('location' => 'xml', 'type' => 'string', 'data' => array('xmlAttribute' => true))
51463 )
51464 ),
51465 array('Foo' => 'test', 'Baz' => 'bar'),
51466 '<test Foo="test"/>'
51467 ),
51468 // Test adding with an array
51469 array(
51470 array(
51471 'parameters' => array(
51472 'Foo' => array('location' => 'xml', 'type' => 'string'),
51473 'Baz' => array(
51474 'type' => 'array',
51475 'location' => 'xml',
51476 'items' => array(
51477 'type' => 'numeric',
51478 'sentAs' => 'Bar'
51479 )
51480 )
51481 )
51482 ),
51483 array('Foo' => 'test', 'Baz' => array(1, 2)),
51484 '<Request><Foo>test</Foo><Baz><Bar>1</Bar><Bar>2</Bar></Baz></Request>'
51485 ),
51486 // Test adding an object
51487 array(
51488 array(
51489 'parameters' => array(
51490 'Foo' => array('location' => 'xml', 'type' => 'string'),
51491 'Baz' => array(
51492 'type' => 'object',
51493 'location' => 'xml',
51494 'properties' => array(
51495 'Bar' => array('type' => 'string'),
51496 'Bam' => array()
51497 )
51498 )
51499 )
51500 ),
51501 array('Foo' => 'test', 'Baz' => array('Bar' => 'abc', 'Bam' => 'foo')),
51502 '<Request><Foo>test</Foo><Baz><Bar>abc</Bar><Bam>foo</Bam></Baz></Request>'
51503 ),
51504 // Add an array that contains an object
51505 array(
51506 array(
51507 'parameters' => array(
51508 'Baz' => array(
51509 'type' => 'array',
51510 'location' => 'xml',
51511 'items' => array(
51512 'type' => 'object',
51513 'sentAs' => 'Bar',
51514 'properties' => array('A' => array(), 'B' => array())
51515 )
51516 )
51517 )
51518 ),
51519 array('Baz' => array(
51520 array('A' => '1', 'B' => '2'),
51521 array('A' => '3', 'B' => '4')
51522 )),
51523 '<Request><Baz><Bar><A>1</A><B>2</B></Bar><Bar><A>3</A><B>4</B></Bar></Baz></Request>'
51524 ),
51525 // Add an object of attributes
51526 array(
51527 array(
51528 'parameters' => array(
51529 'Foo' => array('location' => 'xml', 'type' => 'string'),
51530 'Baz' => array(
51531 'type' => 'object',
51532 'location' => 'xml',
51533 'properties' => array(
51534 'Bar' => array('type' => 'string', 'data' => array('xmlAttribute' => true)),
51535 'Bam' => array()
51536 )
51537 )
51538 )
51539 ),
51540 array('Foo' => 'test', 'Baz' => array('Bar' => 'abc', 'Bam' => 'foo')),
51541 '<Request><Foo>test</Foo><Baz Bar="abc"><Bam>foo</Bam></Baz></Request>'
51542 ),
51543 // Check order doesn't matter
51544 array(
51545 array(
51546 'parameters' => array(
51547 'Foo' => array('location' => 'xml', 'type' => 'string'),
51548 'Baz' => array(
51549 'type' => 'object',
51550 'location' => 'xml',
51551 'properties' => array(
51552 'Bar' => array('type' => 'string', 'data' => array('xmlAttribute' => true)),
51553 'Bam' => array()
51554 )
51555 )
51556 )
51557 ),
51558 array('Foo' => 'test', 'Baz' => array('Bam' => 'foo', 'Bar' => 'abc')),
51559 '<Request><Foo>test</Foo><Baz Bar="abc"><Bam>foo</Bam></Baz></Request>'
51560 ),
51561 // Add values with custom namespaces
51562 array(
51563 array(
51564 'parameters' => array(
51565 'Foo' => array(
51566 'location' => 'xml',
51567 'type' => 'string',
51568 'data' => array(
51569 'xmlNamespace' => 'http://foo.com'
51570 )
51571 )
51572 )
51573 ),
51574 array('Foo' => 'test'),
51575 '<Request><Foo xmlns="http://foo.com">test</Foo></Request>'
51576 ),
51577 // Add attributes with custom namespace prefix
51578 array(
51579 array(
51580 'parameters' => array(
51581 'Wrap' => array(
51582 'type' => 'object',
51583 'location' => 'xml',
51584 'properties' => array(
51585 'Foo' => array(
51586 'type' => 'string',
51587 'sentAs' => 'xsi:baz',
51588 'data' => array(
51589 'xmlNamespace' => 'http://foo.com',
51590 'xmlAttribute' => true
51591 )
51592 )
51593 )
51594 ),
51595 )
51596 ),
51597 array('Wrap' => array(
51598 'Foo' => 'test'
51599 )),
51600 '<Request><Wrap xsi:baz="test" xmlns:xsi="http://foo.com"/></Request>'
51601 ),
51602 // Add nodes with custom namespace prefix
51603 array(
51604 array(
51605 'parameters' => array(
51606 'Wrap' => array(
51607 'type' => 'object',
51608 'location' => 'xml',
51609 'properties' => array(
51610 'Foo' => array(
51611 'type' => 'string',
51612 'sentAs' => 'xsi:Foo',
51613 'data' => array(
51614 'xmlNamespace' => 'http://foobar.com'
51615 )
51616 )
51617 )
51618 ),
51619 )
51620 ),
51621 array('Wrap' => array(
51622 'Foo' => 'test'
51623 )),
51624 '<Request><Wrap><xsi:Foo xmlns:xsi="http://foobar.com">test</xsi:Foo></Wrap></Request>'
51625 ),
51626 array(
51627 array(
51628 'parameters' => array(
51629 'Foo' => array(
51630 'location' => 'xml',
51631 'type' => 'string',
51632 'data' => array(
51633 'xmlNamespace' => 'http://foo.com'
51634 )
51635 )
51636 )
51637 ),
51638 array('Foo' => '<h1>This is a title</h1>'),
51639 '<Request><Foo xmlns="http://foo.com"><![CDATA[<h1>This is a title</h1>]]></Foo></Request>'
51640 ),
51641 // Flat array at top level
51642 array(
51643 array(
51644 'parameters' => array(
51645 'Bars' => array(
51646 'type' => 'array',
51647 'data' => array('xmlFlattened' => true),
51648 'location' => 'xml',
51649 'items' => array(
51650 'type' => 'object',
51651 'sentAs' => 'Bar',
51652 'properties' => array(
51653 'A' => array(),
51654 'B' => array()
51655 )
51656 )
51657 ),
51658 'Boos' => array(
51659 'type' => 'array',
51660 'data' => array('xmlFlattened' => true),
51661 'location' => 'xml',
51662 'items' => array(
51663 'sentAs' => 'Boo',
51664 'type' => 'string'
51665 )
51666 )
51667 )
51668 ),
51669 array(
51670 'Bars' => array(
51671 array('A' => '1', 'B' => '2'),
51672 array('A' => '3', 'B' => '4')
51673 ),
51674 'Boos' => array('test', '123')
51675 ),
51676 '<Request><Bar><A>1</A><B>2</B></Bar><Bar><A>3</A><B>4</B></Bar><Boo>test</Boo><Boo>123</Boo></Request>'
51677 ),
51678 // Nested flat arrays
51679 array(
51680 array(
51681 'parameters' => array(
51682 'Delete' => array(
51683 'type' => 'object',
51684 'location' => 'xml',
51685 'properties' => array(
51686 'Items' => array(
51687 'type' => 'array',
51688 'data' => array('xmlFlattened' => true),
51689 'items' => array(
51690 'type' => 'object',
51691 'sentAs' => 'Item',
51692 'properties' => array(
51693 'A' => array(),
51694 'B' => array()
51695 )
51696 )
51697 )
51698 )
51699 )
51700 )
51701 ),
51702 array(
51703 'Delete' => array(
51704 'Items' => array(
51705 array('A' => '1', 'B' => '2'),
51706 array('A' => '3', 'B' => '4')
51707 )
51708 )
51709 ),
51710 '<Request><Delete><Item><A>1</A><B>2</B></Item><Item><A>3</A><B>4</B></Item></Delete></Request>'
51711 )
51712 );
51713 }
51714
51715 /**
51716 * @dataProvider xmlProvider
51717 */
51718 public function testSerializesXml(array $operation, array $input, $xml)
51719 {
51720 $operation = new Operation($operation);
51721 $command = $this->getMockBuilder('Guzzle\Service\Command\OperationCommand')
51722 ->setConstructorArgs(array($input, $operation))
51723 ->getMockForAbstractClass();
51724 $command->setClient(new Client('http://www.test.com/some/path.php'));
51725 $request = $command->prepare();
51726 if (!empty($input)) {
51727 $this->assertEquals('application/xml', (string) $request->getHeader('Content-Type'));
51728 } else {
51729 $this->assertNull($request->getHeader('Content-Type'));
51730 }
51731 $body = str_replace(array("\n", "<?xml version=\"1.0\"?>"), '', (string) $request->getBody());
51732 $this->assertEquals($xml, $body);
51733 }
51734
51735 public function testAddsContentTypeAndTopLevelValues()
51736 {
51737 $operation = new Operation(array(
51738 'data' => array(
51739 'xmlRoot' => array(
51740 'name' => 'test',
51741 'namespaces' => array(
51742 'xsi' => 'http://foo.com'
51743 )
51744 )
51745 ),
51746 'parameters' => array(
51747 'Foo' => array('location' => 'xml', 'type' => 'string'),
51748 'Baz' => array('location' => 'xml', 'type' => 'string')
51749 )
51750 ));
51751
51752 $command = $this->getMockBuilder('Guzzle\Service\Command\OperationCommand')
51753 ->setConstructorArgs(array(array(
51754 'Foo' => 'test',
51755 'Baz' => 'bar'
51756 ), $operation))
51757 ->getMockForAbstractClass();
51758
51759 $command->setClient(new Client());
51760 $request = $command->prepare();
51761 $this->assertEquals('application/xml', (string) $request->getHeader('Content-Type'));
51762 $this->assertEquals(
51763 '<?xml version="1.0"?>' . "\n"
51764 . '<test xmlns:xsi="http://foo.com"><Foo>test</Foo><Baz>bar</Baz></test>' . "\n",
51765 (string) $request->getBody()
51766 );
51767 }
51768
51769 public function testCanChangeContentType()
51770 {
51771 $visitor = new XmlVisitor();
51772 $visitor->setContentTypeHeader('application/foo');
51773 $this->assertEquals('application/foo', $this->readAttribute($visitor, 'contentType'));
51774 }
51775
51776 public function testCanAddArrayOfSimpleTypes()
51777 {
51778 $request = new EntityEnclosingRequest('POST', 'http://foo.com');
51779 $visitor = new XmlVisitor();
51780 $param = new Parameter(array(
51781 'type' => 'object',
51782 'location' => 'xml',
51783 'name' => 'Out',
51784 'properties' => array(
51785 'Nodes' => array(
51786 'required' => true,
51787 'type' => 'array',
51788 'min' => 1,
51789 'items' => array('type' => 'string', 'sentAs' => 'Node')
51790 )
51791 )
51792 ));
51793
51794 $param->setParent(new Operation(array(
51795 'data' => array(
51796 'xmlRoot' => array(
51797 'name' => 'Test',
51798 'namespaces' => array(
51799 'https://foo/'
51800 )
51801 )
51802 )
51803 )));
51804
51805 $value = array('Nodes' => array('foo', 'baz'));
51806 $this->assertTrue($this->validator->validate($param, $value));
51807 $visitor->visit($this->command, $request, $param, $value);
51808 $visitor->after($this->command, $request);
51809
51810 $this->assertEquals(
51811 "<?xml version=\"1.0\"?>\n"
51812 . "<Test xmlns=\"https://foo/\"><Out><Nodes><Node>foo</Node><Node>baz</Node></Nodes></Out></Test>\n",
51813 (string) $request->getBody()
51814 );
51815 }
51816
51817 public function testCanAddMultipleNamespacesToRoot()
51818 {
51819 $operation = new Operation(array(
51820 'data' => array(
51821 'xmlRoot' => array(
51822 'name' => 'Hi',
51823 'namespaces' => array(
51824 'xsi' => 'http://foo.com',
51825 'foo' => 'http://foobar.com'
51826 )
51827 )
51828 ),
51829 'parameters' => array(
51830 'Foo' => array('location' => 'xml', 'type' => 'string')
51831 )
51832 ));
51833
51834 $command = $this->getMockBuilder('Guzzle\Service\Command\OperationCommand')
51835 ->setConstructorArgs(array(array(
51836 'Foo' => 'test'
51837 ), $operation))
51838 ->getMockForAbstractClass();
51839
51840 $command->setClient(new Client());
51841 $request = $command->prepare();
51842 $this->assertEquals('application/xml', (string) $request->getHeader('Content-Type'));
51843 $this->assertEquals(
51844 '<?xml version="1.0"?>' . "\n"
51845 . '<Hi xmlns:xsi="http://foo.com" xmlns:foo="http://foobar.com"><Foo>test</Foo></Hi>' . "\n",
51846 (string) $request->getBody()
51847 );
51848 }
51849
51850 public function testValuesAreFiltered()
51851 {
51852 $operation = new Operation(array(
51853 'parameters' => array(
51854 'Foo' => array(
51855 'location' => 'xml',
51856 'type' => 'string',
51857 'filters' => array('strtoupper')
51858 ),
51859 'Bar' => array(
51860 'location' => 'xml',
51861 'type' => 'object',
51862 'properties' => array(
51863 'Baz' => array(
51864 'filters' => array('strtoupper')
51865 )
51866 )
51867 )
51868 )
51869 ));
51870
51871 $command = $this->getMockBuilder('Guzzle\Service\Command\OperationCommand')
51872 ->setConstructorArgs(array(array(
51873 'Foo' => 'test',
51874 'Bar' => array(
51875 'Baz' => 'abc'
51876 )
51877 ), $operation))
51878 ->getMockForAbstractClass();
51879
51880 $command->setClient(new Client());
51881 $request = $command->prepare();
51882 $this->assertEquals(
51883 '<?xml version="1.0"?>' . "\n"
51884 . '<Request><Foo>TEST</Foo><Bar><Baz>ABC</Baz></Bar></Request>' . "\n",
51885 (string) $request->getBody()
51886 );
51887 }
51888
51889 public function testSkipsNullValues()
51890 {
51891 $operation = new Operation(array(
51892 'parameters' => array(
51893 'Foo' => array(
51894 'location' => 'xml',
51895 'type' => 'string'
51896 ),
51897 'Bar' => array(
51898 'location' => 'xml',
51899 'type' => 'object',
51900 'properties' => array(
51901 'Baz' => array(),
51902 'Bam' => array(),
51903 )
51904 ),
51905 'Arr' => array(
51906 'type' => 'array',
51907 'items' => array(
51908 'type' => 'string'
51909 )
51910 )
51911 )
51912 ));
51913
51914 $command = $this->getMockBuilder('Guzzle\Service\Command\OperationCommand')
51915 ->setConstructorArgs(array(array(
51916 'Foo' => null,
51917 'Bar' => array(
51918 'Bar' => null,
51919 'Bam' => 'test'
51920 ),
51921 'Arr' => array(null)
51922 ), $operation))
51923 ->getMockForAbstractClass();
51924
51925 $command->setClient(new Client());
51926 $request = $command->prepare();
51927 $this->assertEquals(
51928 '<?xml version="1.0"?>' . "\n"
51929 . '<Request><Bar><Bam>test</Bam></Bar></Request>' . "\n",
51930 (string) $request->getBody()
51931 );
51932 }
51933
51934 public function testAllowsXmlEncoding()
51935 {
51936 $operation = new Operation(array(
51937 'data' => array(
51938 'xmlEncoding' => 'UTF-8'
51939 ),
51940 'parameters' => array(
51941 'Foo' => array('location' => 'xml')
51942 )
51943 ));
51944 $command = $this->getMockBuilder('Guzzle\Service\Command\OperationCommand')
51945 ->setConstructorArgs(array(array('Foo' => 'test'), $operation))
51946 ->getMockForAbstractClass();
51947 $command->setClient(new Client());
51948 $request = $command->prepare();
51949 $this->assertEquals(
51950 '<?xml version="1.0" encoding="UTF-8"?>' . "\n"
51951 . '<Request><Foo>test</Foo></Request>' . "\n",
51952 (string) $request->getBody()
51953 );
51954 }
51955
51956 public function testAllowsSendingXmlPayloadIfNoXmlParamsWereSet()
51957 {
51958 $operation = new Operation(array(
51959 'httpMethod' => 'POST',
51960 'data' => array('xmlAllowEmpty' => true),
51961 'parameters' => array('Foo' => array('location' => 'xml'))
51962 ));
51963 $command = $this->getMockBuilder('Guzzle\Service\Command\OperationCommand')
51964 ->setConstructorArgs(array(array(), $operation))
51965 ->getMockForAbstractClass();
51966 $command->setClient(new Client('http://foo.com'));
51967 $request = $command->prepare();
51968 $this->assertEquals(
51969 '<?xml version="1.0"?>' . "\n"
51970 . '<Request/>' . "\n",
51971 (string) $request->getBody()
51972 );
51973 }
51974 }
51975 <?php
51976
51977 namespace Guzzle\Tests\Service\Command\LocationVisitor\Request;
51978
51979 use Guzzle\Http\Message\EntityEnclosingRequest;
51980 use Guzzle\Service\Description\Operation;
51981 use Guzzle\Service\Description\Parameter;
51982 use Guzzle\Service\Description\SchemaValidator;
51983 use Guzzle\Service\Command\OperationCommand;
51984 use Guzzle\Tests\Service\Mock\Command\MockCommand;
51985 use Guzzle\Tests\Service\Mock\MockClient;
51986
51987 abstract class AbstractVisitorTestCase extends \Guzzle\Tests\GuzzleTestCase
51988 {
51989 protected $command;
51990 protected $request;
51991 protected $param;
51992 protected $validator;
51993
51994 public function setUp()
51995 {
51996 $this->command = new MockCommand();
51997 $this->request = new EntityEnclosingRequest('POST', 'http://www.test.com/some/path.php');
51998 $this->validator = new SchemaValidator();
51999 }
52000
52001 protected function getCommand($location)
52002 {
52003 $command = new OperationCommand(array(), $this->getNestedCommand($location));
52004 $command->setClient(new MockClient());
52005
52006 return $command;
52007 }
52008
52009 protected function getNestedCommand($location)
52010 {
52011 return new Operation(array(
52012 'httpMethod' => 'POST',
52013 'parameters' => array(
52014 'foo' => new Parameter(array(
52015 'type' => 'object',
52016 'location' => $location,
52017 'sentAs' => 'Foo',
52018 'required' => true,
52019 'properties' => array(
52020 'test' => array(
52021 'type' => 'object',
52022 'required' => true,
52023 'properties' => array(
52024 'baz' => array(
52025 'type' => 'boolean',
52026 'default' => true
52027 ),
52028 'jenga' => array(
52029 'type' => 'string',
52030 'default' => 'hello',
52031 'sentAs' => 'Jenga_Yall!',
52032 'filters' => array('strtoupper')
52033 )
52034 )
52035 ),
52036 'bar' => array('default' => 123)
52037 ),
52038 'additionalProperties' => array(
52039 'type' => 'string',
52040 'filters' => array('strtoupper'),
52041 'location' => $location
52042 )
52043 )),
52044 'arr' => new Parameter(array(
52045 'type' => 'array',
52046 'location' => $location,
52047 'items' => array(
52048 'type' => 'string',
52049 'filters' => array('strtoupper')
52050 )
52051 )),
52052 )
52053 ));
52054 }
52055
52056 protected function getCommandWithArrayParamAndFilters()
52057 {
52058 $operation = new Operation(array(
52059 'httpMethod' => 'POST',
52060 'parameters' => array(
52061 'foo' => new Parameter(array(
52062 'type' => 'string',
52063 'location' => 'query',
52064 'sentAs' => 'Foo',
52065 'required' => true,
52066 'default' => 'bar',
52067 'filters' => array('strtoupper')
52068 )),
52069 'arr' => new Parameter(array(
52070 'type' => 'array',
52071 'location' => 'query',
52072 'sentAs' => 'Arr',
52073 'required' => true,
52074 'default' => array(123, 456, 789),
52075 'filters' => array(array('method' => 'implode', 'args' => array(',', '@value')))
52076 ))
52077 )
52078 ));
52079 $command = new OperationCommand(array(), $operation);
52080 $command->setClient(new MockClient());
52081
52082 return $command;
52083 }
52084 }
52085 <?php
52086
52087 namespace Guzzle\Tests\Service\Command\LocationVisitor\Request;
52088
52089 use Guzzle\Service\Command\LocationVisitor\Request\ResponseBodyVisitor as Visitor;
52090
52091 /**
52092 * @covers Guzzle\Service\Command\LocationVisitor\Request\ResponseBodyVisitor
52093 */
52094 class ResponseBodyVisitorTest extends AbstractVisitorTestCase
52095 {
52096 public function testVisitsLocation()
52097 {
52098 $visitor = new Visitor();
52099 $param = $this->getNestedCommand('response_body')->getParam('foo');
52100 $visitor->visit($this->command, $this->request, $param, sys_get_temp_dir() . '/foo.txt');
52101 $body = $this->readAttribute($this->request, 'responseBody');
52102 $this->assertContains('/foo.txt', $body->getUri());
52103 }
52104 }
52105 <?php
52106
52107 namespace Guzzle\Tests\Service\Command\LocationVisitor\Request;
52108
52109 use Guzzle\Service\Command\LocationVisitor\Request\PostFieldVisitor as Visitor;
52110
52111 /**
52112 * @covers Guzzle\Service\Command\LocationVisitor\Request\PostFieldVisitor
52113 */
52114 class PostFieldVisitorTest extends AbstractVisitorTestCase
52115 {
52116 public function testVisitsLocation()
52117 {
52118 $visitor = new Visitor();
52119 $param = $this->getNestedCommand('postField')->getParam('foo');
52120 $visitor->visit($this->command, $this->request, $param->setSentAs('test'), '123');
52121 $this->assertEquals('123', (string) $this->request->getPostField('test'));
52122 }
52123
52124 public function testRecursivelyBuildsPostFields()
52125 {
52126 $command = $this->getCommand('postField');
52127 $request = $command->prepare();
52128 $visitor = new Visitor();
52129 $param = $command->getOperation()->getParam('foo');
52130 $visitor->visit($command, $request, $param, $command['foo']);
52131 $visitor->after($command, $request);
52132 $this->assertEquals(
52133 'Foo[test][baz]=1&Foo[test][Jenga_Yall!]=HELLO&Foo[bar]=123',
52134 rawurldecode((string) $request->getPostFields())
52135 );
52136 }
52137 }
52138 <?php
52139
52140 namespace Guzzle\Tests\Service\Command\LocationVisitor\Response;
52141
52142 use Guzzle\Service\Description\Parameter;
52143 use Guzzle\Http\Message\Response;
52144 use Guzzle\Service\Command\LocationVisitor\Response\HeaderVisitor as Visitor;
52145
52146 /**
52147 * @covers Guzzle\Service\Command\LocationVisitor\Response\HeaderVisitor
52148 */
52149 class HeaderVisitorTest extends AbstractResponseVisitorTest
52150 {
52151 public function testVisitsLocation()
52152 {
52153 $visitor = new Visitor();
52154 $param = new Parameter(array(
52155 'location' => 'header',
52156 'name' => 'ContentType',
52157 'sentAs' => 'Content-Type'
52158 ));
52159 $visitor->visit($this->command, $this->response, $param, $this->value);
52160 $this->assertEquals('text/plain', $this->value['ContentType']);
52161 }
52162
52163 public function testVisitsLocationWithFilters()
52164 {
52165 $visitor = new Visitor();
52166 $param = new Parameter(array(
52167 'location' => 'header',
52168 'name' => 'Content-Type',
52169 'filters' => array('strtoupper')
52170 ));
52171 $visitor->visit($this->command, $this->response, $param, $this->value);
52172 $this->assertEquals('TEXT/PLAIN', $this->value['Content-Type']);
52173 }
52174
52175 public function testVisitsMappedPrefixHeaders()
52176 {
52177 $visitor = new Visitor();
52178 $param = new Parameter(array(
52179 'location' => 'header',
52180 'name' => 'Metadata',
52181 'sentAs' => 'X-Baz-',
52182 'type' => 'object',
52183 'additionalProperties' => array(
52184 'type' => 'string'
52185 )
52186 ));
52187 $response = new Response(200, array(
52188 'X-Baz-Test' => 'ABC',
52189 'X-Baz-Bar' => array('123', '456'),
52190 'Content-Length' => 3
52191 ), 'Foo');
52192 $visitor->visit($this->command, $response, $param, $this->value);
52193 $this->assertEquals(array(
52194 'Metadata' => array(
52195 'Test' => 'ABC',
52196 'Bar' => array('123', '456')
52197 )
52198 ), $this->value);
52199 }
52200
52201 /**
52202 * @group issue-399
52203 * @link https://github.com/guzzle/guzzle/issues/399
52204 */
52205 public function testDiscardingUnknownHeaders()
52206 {
52207 $visitor = new Visitor();
52208 $param = new Parameter(array(
52209 'location' => 'header',
52210 'name' => 'Content-Type',
52211 'additionalParameters' => false
52212 ));
52213 $visitor->visit($this->command, $this->response, $param, $this->value);
52214 $this->assertEquals('text/plain', $this->value['Content-Type']);
52215 $this->assertArrayNotHasKey('X-Foo', $this->value);
52216 }
52217
52218 /**
52219 * @group issue-399
52220 * @link https://github.com/guzzle/guzzle/issues/399
52221 */
52222 public function testDiscardingUnknownPropertiesWithAliasing()
52223 {
52224 $visitor = new Visitor();
52225 $param = new Parameter(array(
52226 'location' => 'header',
52227 'name' => 'ContentType',
52228 'sentAs' => 'Content-Type',
52229 'additionalParameters' => false
52230 ));
52231 $visitor->visit($this->command, $this->response, $param, $this->value);
52232 $this->assertEquals('text/plain', $this->value['ContentType']);
52233 $this->assertArrayNotHasKey('X-Foo', $this->value);
52234 }
52235 }
52236 <?php
52237
52238 namespace Guzzle\Tests\Service\Command\LocationVisitor\Response;
52239
52240 use Guzzle\Tests\Service\Mock\Command\MockCommand;
52241 use Guzzle\Http\Message\Response;
52242
52243 abstract class AbstractResponseVisitorTest extends \Guzzle\Tests\GuzzleTestCase
52244 {
52245 /** @var Response */
52246 protected $response;
52247
52248 /** @var MockCommand */
52249 protected $command;
52250
52251 /** @var array */
52252 protected $value;
52253
52254 public function setUp()
52255 {
52256 $this->value = array();
52257 $this->command = new MockCommand();
52258 $this->response = new Response(200, array(
52259 'X-Foo' => 'bar',
52260 'Content-Length' => 3,
52261 'Content-Type' => 'text/plain'
52262 ), 'Foo');
52263 }
52264 }
52265 <?php
52266
52267 namespace Guzzle\Tests\Service\Command\LocationVisitor\Response;
52268
52269 use Guzzle\Service\Description\Parameter;
52270 use Guzzle\Http\Message\Response;
52271 use Guzzle\Service\Command\LocationVisitor\Response\BodyVisitor as Visitor;
52272
52273 /**
52274 * @covers Guzzle\Service\Command\LocationVisitor\Response\BodyVisitor
52275 */
52276 class BodyVisitorTest extends AbstractResponseVisitorTest
52277 {
52278 public function testVisitsLocation()
52279 {
52280 $visitor = new Visitor();
52281 $param = new Parameter(array('location' => 'body', 'name' => 'foo'));
52282 $visitor->visit($this->command, $this->response, $param, $this->value);
52283 $this->assertEquals('Foo', (string) $this->value['foo']);
52284 }
52285 }
52286 <?php
52287
52288 namespace Guzzle\Tests\Service\Command\LocationVisitor\Response;
52289
52290 use Guzzle\Service\Description\Parameter;
52291 use Guzzle\Http\Message\Response;
52292 use Guzzle\Service\Command\LocationVisitor\Response\JsonVisitor as Visitor;
52293
52294 /**
52295 * @covers Guzzle\Service\Command\LocationVisitor\Response\JsonVisitor
52296 */
52297 class JsonVisitorTest extends AbstractResponseVisitorTest
52298 {
52299 public function testBeforeMethodParsesXml()
52300 {
52301 $visitor = new Visitor();
52302 $command = $this->getMockBuilder('Guzzle\Service\Command\AbstractCommand')
52303 ->setMethods(array('getResponse'))
52304 ->getMockForAbstractClass();
52305 $command->expects($this->once())
52306 ->method('getResponse')
52307 ->will($this->returnValue(new Response(200, null, '{"foo":"bar"}')));
52308 $result = array();
52309 $visitor->before($command, $result);
52310 $this->assertEquals(array('foo' => 'bar'), $result);
52311 }
52312
52313 public function testVisitsLocation()
52314 {
52315 $visitor = new Visitor();
52316 $param = new Parameter(array(
52317 'name' => 'foo',
52318 'type' => 'array',
52319 'items' => array(
52320 'filters' => 'strtoupper',
52321 'type' => 'string'
52322 )
52323 ));
52324 $this->value = array('foo' => array('a', 'b', 'c'));
52325 $visitor->visit($this->command, $this->response, $param, $this->value);
52326 $this->assertEquals(array('A', 'B', 'C'), $this->value['foo']);
52327 }
52328
52329 public function testRenamesTopLevelValues()
52330 {
52331 $visitor = new Visitor();
52332 $param = new Parameter(array(
52333 'name' => 'foo',
52334 'sentAs' => 'Baz',
52335 'type' => 'string',
52336 ));
52337 $this->value = array('Baz' => 'test');
52338 $visitor->visit($this->command, $this->response, $param, $this->value);
52339 $this->assertEquals(array('foo' => 'test'), $this->value);
52340 }
52341
52342 public function testRenamesDoesNotFailForNonExistentKey()
52343 {
52344 $visitor = new Visitor();
52345 $param = new Parameter(array(
52346 'name' => 'foo',
52347 'type' => 'object',
52348 'properties' => array(
52349 'bar' => array(
52350 'name' => 'bar',
52351 'sentAs' => 'baz',
52352 ),
52353 ),
52354 ));
52355 $this->value = array('foo' => array('unknown' => 'Unknown'));
52356 $visitor->visit($this->command, $this->response, $param, $this->value);
52357 $this->assertEquals(array('foo' => array('unknown' => 'Unknown')), $this->value);
52358 }
52359
52360 public function testTraversesObjectsAndAppliesFilters()
52361 {
52362 $visitor = new Visitor();
52363 $param = new Parameter(array(
52364 'name' => 'foo',
52365 'type' => 'object',
52366 'properties' => array(
52367 'foo' => array('filters' => 'strtoupper'),
52368 'bar' => array('filters' => 'strtolower')
52369 )
52370 ));
52371 $this->value = array('foo' => array('foo' => 'hello', 'bar' => 'THERE'));
52372 $visitor->visit($this->command, $this->response, $param, $this->value);
52373 $this->assertEquals(array('foo' => 'HELLO', 'bar' => 'there'), $this->value['foo']);
52374 }
52375
52376 /**
52377 * @group issue-399
52378 * @link https://github.com/guzzle/guzzle/issues/399
52379 */
52380 public function testDiscardingUnknownProperties()
52381 {
52382 $visitor = new Visitor();
52383 $param = new Parameter(array(
52384 'name' => 'foo',
52385 'type' => 'object',
52386 'additionalProperties' => false,
52387 'properties' => array(
52388 'bar' => array(
52389 'type' => 'string',
52390 'name' => 'bar',
52391 ),
52392 ),
52393 ));
52394 $this->value = array('foo' => array('bar' => 15, 'unknown' => 'Unknown'));
52395 $visitor->visit($this->command, $this->response, $param, $this->value);
52396 $this->assertEquals(array('foo' => array('bar' => 15)), $this->value);
52397 }
52398
52399 /**
52400 * @group issue-399
52401 * @link https://github.com/guzzle/guzzle/issues/399
52402 */
52403 public function testDiscardingUnknownPropertiesWithAliasing()
52404 {
52405 $visitor = new Visitor();
52406 $param = new Parameter(array(
52407 'name' => 'foo',
52408 'type' => 'object',
52409 'additionalProperties' => false,
52410 'properties' => array(
52411 'bar' => array(
52412 'name' => 'bar',
52413 'sentAs' => 'baz',
52414 ),
52415 ),
52416 ));
52417 $this->value = array('foo' => array('baz' => 15, 'unknown' => 'Unknown'));
52418 $visitor->visit($this->command, $this->response, $param, $this->value);
52419 $this->assertEquals(array('foo' => array('bar' => 15)), $this->value);
52420 }
52421
52422 }
52423 <?php
52424
52425 namespace Guzzle\Tests\Service\Command\LocationVisitor\Response;
52426
52427 use Guzzle\Service\Description\Parameter;
52428 use Guzzle\Http\Message\Response;
52429 use Guzzle\Service\Command\LocationVisitor\Response\XmlVisitor as Visitor;
52430
52431 /**
52432 * @covers Guzzle\Service\Command\LocationVisitor\Response\XmlVisitor
52433 */
52434 class XmlVisitorTest extends AbstractResponseVisitorTest
52435 {
52436 public function testBeforeMethodParsesXml()
52437 {
52438 $visitor = new Visitor();
52439 $command = $this->getMockBuilder('Guzzle\Service\Command\AbstractCommand')
52440 ->setMethods(array('getResponse'))
52441 ->getMockForAbstractClass();
52442 $command->expects($this->once())
52443 ->method('getResponse')
52444 ->will($this->returnValue(new Response(200, null, '<foo><Bar>test</Bar></foo>')));
52445 $result = array();
52446 $visitor->before($command, $result);
52447 $this->assertEquals(array('Bar' => 'test'), $result);
52448 }
52449
52450 public function testBeforeMethodParsesXmlWithNamespace()
52451 {
52452 $this->markTestSkipped("Response/XmlVisitor cannot accept 'xmlns' in response, see #368 (http://git.io/USa1mA).");
52453
52454 $visitor = new Visitor();
52455 $command = $this->getMockBuilder('Guzzle\Service\Command\AbstractCommand')
52456 ->setMethods(array('getResponse'))
52457 ->getMockForAbstractClass();
52458 $command->expects($this->once())
52459 ->method('getResponse')
52460 ->will($this->returnValue(new Response(200, null, '<foo xmlns="urn:foo"><bar:Bar xmlns:bar="urn:bar">test</bar:Bar></foo>')));
52461 $result = array();
52462 $visitor->before($command, $result);
52463 $this->assertEquals(array('Bar' => 'test'), $result);
52464 }
52465
52466 public function testBeforeMethodParsesNestedXml()
52467 {
52468 $visitor = new Visitor();
52469 $command = $this->getMockBuilder('Guzzle\Service\Command\AbstractCommand')
52470 ->setMethods(array('getResponse'))
52471 ->getMockForAbstractClass();
52472 $command->expects($this->once())
52473 ->method('getResponse')
52474 ->will($this->returnValue(new Response(200, null, '<foo><Items><Bar>test</Bar></Items></foo>')));
52475 $result = array();
52476 $visitor->before($command, $result);
52477 $this->assertEquals(array('Items' => array('Bar' => 'test')), $result);
52478 }
52479
52480 public function testCanExtractAndRenameTopLevelXmlValues()
52481 {
52482 $visitor = new Visitor();
52483 $param = new Parameter(array(
52484 'location' => 'xml',
52485 'name' => 'foo',
52486 'sentAs' => 'Bar'
52487 ));
52488 $value = array('Bar' => 'test');
52489 $visitor->visit($this->command, $this->response, $param, $value);
52490 $this->assertArrayHasKey('foo', $value);
52491 $this->assertEquals('test', $value['foo']);
52492 }
52493
52494 public function testEnsuresRepeatedArraysAreInCorrectLocations()
52495 {
52496 $visitor = new Visitor();
52497 $param = new Parameter(array(
52498 'location' => 'xml',
52499 'name' => 'foo',
52500 'sentAs' => 'Foo',
52501 'type' => 'array',
52502 'items' => array(
52503 'type' => 'object',
52504 'properties' => array(
52505 'Bar' => array('type' => 'string'),
52506 'Baz' => array('type' => 'string'),
52507 'Bam' => array('type' => 'string')
52508 )
52509 )
52510 ));
52511
52512 $xml = new \SimpleXMLElement('<Test><Foo><Bar>1</Bar><Baz>2</Baz></Foo></Test>');
52513 $value = json_decode(json_encode($xml), true);
52514 $visitor->visit($this->command, $this->response, $param, $value);
52515 $this->assertEquals(array(
52516 'foo' => array(
52517 array (
52518 'Bar' => '1',
52519 'Baz' => '2'
52520 )
52521 )
52522 ), $value);
52523 }
52524
52525 public function testEnsuresFlatArraysAreFlat()
52526 {
52527 $visitor = new Visitor();
52528 $param = new Parameter(array(
52529 'location' => 'xml',
52530 'name' => 'foo',
52531 'type' => 'array',
52532 'items' => array('type' => 'string')
52533 ));
52534
52535 $value = array('foo' => array('bar', 'baz'));
52536 $visitor->visit($this->command, $this->response, $param, $value);
52537 $this->assertEquals(array('foo' => array('bar', 'baz')), $value);
52538
52539 $value = array('foo' => 'bar');
52540 $visitor->visit($this->command, $this->response, $param, $value);
52541 $this->assertEquals(array('foo' => array('bar')), $value);
52542 }
52543
52544 public function xmlDataProvider()
52545 {
52546 $param = new Parameter(array(
52547 'location' => 'xml',
52548 'name' => 'Items',
52549 'type' => 'array',
52550 'items' => array(
52551 'type' => 'object',
52552 'name' => 'Item',
52553 'properties' => array(
52554 'Bar' => array('type' => 'string'),
52555 'Baz' => array('type' => 'string')
52556 )
52557 )
52558 ));
52559
52560 return array(
52561 array($param, '<Test><Items><Item><Bar>1</Bar></Item><Item><Bar>2</Bar></Item></Items></Test>', array(
52562 'Items' => array(
52563 array('Bar' => 1),
52564 array('Bar' => 2)
52565 )
52566 )),
52567 array($param, '<Test><Items><Item><Bar>1</Bar></Item></Items></Test>', array(
52568 'Items' => array(
52569 array('Bar' => 1)
52570 )
52571 )),
52572 array($param, '<Test><Items /></Test>', array(
52573 'Items' => array()
52574 ))
52575 );
52576 }
52577
52578 /**
52579 * @dataProvider xmlDataProvider
52580 */
52581 public function testEnsuresWrappedArraysAreInCorrectLocations($param, $xml, $result)
52582 {
52583 $visitor = new Visitor();
52584 $xml = new \SimpleXMLElement($xml);
52585 $value = json_decode(json_encode($xml), true);
52586 $visitor->visit($this->command, $this->response, $param, $value);
52587 $this->assertEquals($result, $value);
52588 }
52589
52590 public function testCanRenameValues()
52591 {
52592 $visitor = new Visitor();
52593 $param = new Parameter(array(
52594 'name' => 'TerminatingInstances',
52595 'type' => 'array',
52596 'location' => 'xml',
52597 'sentAs' => 'instancesSet',
52598 'items' => array(
52599 'name' => 'item',
52600 'type' => 'object',
52601 'sentAs' => 'item',
52602 'properties' => array(
52603 'InstanceId' => array(
52604 'type' => 'string',
52605 'sentAs' => 'instanceId',
52606 ),
52607 'CurrentState' => array(
52608 'type' => 'object',
52609 'sentAs' => 'currentState',
52610 'properties' => array(
52611 'Code' => array(
52612 'type' => 'numeric',
52613 'sentAs' => 'code',
52614 ),
52615 'Name' => array(
52616 'type' => 'string',
52617 'sentAs' => 'name',
52618 ),
52619 ),
52620 ),
52621 'PreviousState' => array(
52622 'type' => 'object',
52623 'sentAs' => 'previousState',
52624 'properties' => array(
52625 'Code' => array(
52626 'type' => 'numeric',
52627 'sentAs' => 'code',
52628 ),
52629 'Name' => array(
52630 'type' => 'string',
52631 'sentAs' => 'name',
52632 ),
52633 ),
52634 ),
52635 ),
52636 )
52637 ));
52638
52639 $value = array(
52640 'instancesSet' => array (
52641 'item' => array (
52642 'instanceId' => 'i-3ea74257',
52643 'currentState' => array(
52644 'code' => '32',
52645 'name' => 'shutting-down',
52646 ),
52647 'previousState' => array(
52648 'code' => '16',
52649 'name' => 'running',
52650 ),
52651 ),
52652 )
52653 );
52654
52655 $visitor->visit($this->command, $this->response, $param, $value);
52656
52657 $this->assertEquals(array(
52658 'TerminatingInstances' => array(
52659 array(
52660 'InstanceId' => 'i-3ea74257',
52661 'CurrentState' => array(
52662 'Code' => '32',
52663 'Name' => 'shutting-down',
52664 ),
52665 'PreviousState' => array(
52666 'Code' => '16',
52667 'Name' => 'running',
52668 )
52669 )
52670 )
52671 ), $value);
52672 }
52673
52674 public function testCanRenameAttributes()
52675 {
52676 $visitor = new Visitor();
52677 $param = new Parameter(array(
52678 'name' => 'RunningQueues',
52679 'type' => 'array',
52680 'location' => 'xml',
52681 'items' => array(
52682 'type' => 'object',
52683 'sentAs' => 'item',
52684 'properties' => array(
52685 'QueueId' => array(
52686 'type' => 'string',
52687 'sentAs' => 'queue_id',
52688 'data' => array(
52689 'xmlAttribute' => true,
52690 ),
52691 ),
52692 'CurrentState' => array(
52693 'type' => 'object',
52694 'properties' => array(
52695 'Code' => array(
52696 'type' => 'numeric',
52697 'sentAs' => 'code',
52698 'data' => array(
52699 'xmlAttribute' => true,
52700 ),
52701 ),
52702 'Name' => array(
52703 'sentAs' => 'name',
52704 'data' => array(
52705 'xmlAttribute' => true,
52706 ),
52707 ),
52708 ),
52709 ),
52710 'PreviousState' => array(
52711 'type' => 'object',
52712 'properties' => array(
52713 'Code' => array(
52714 'type' => 'numeric',
52715 'sentAs' => 'code',
52716 'data' => array(
52717 'xmlAttribute' => true,
52718 ),
52719 ),
52720 'Name' => array(
52721 'sentAs' => 'name',
52722 'data' => array(
52723 'xmlAttribute' => true,
52724 ),
52725 ),
52726 ),
52727 ),
52728 ),
52729 )
52730 ));
52731
52732 $xml = '<wrap><RunningQueues><item queue_id="q-3ea74257"><CurrentState code="32" name="processing" /><PreviousState code="16" name="wait" /></item></RunningQueues></wrap>';
52733 $value = json_decode(json_encode(new \SimpleXMLElement($xml)), true);
52734 $visitor->visit($this->command, $this->response, $param, $value);
52735
52736 $this->assertEquals(array(
52737 'RunningQueues' => array(
52738 array(
52739 'QueueId' => 'q-3ea74257',
52740 'CurrentState' => array(
52741 'Code' => '32',
52742 'Name' => 'processing',
52743 ),
52744 'PreviousState' => array(
52745 'Code' => '16',
52746 'Name' => 'wait',
52747 ),
52748 ),
52749 )
52750 ), $value);
52751 }
52752
52753 public function testAddsEmptyArraysWhenValueIsMissing()
52754 {
52755 $visitor = new Visitor();
52756 $param = new Parameter(array(
52757 'name' => 'Foo',
52758 'type' => 'array',
52759 'location' => 'xml',
52760 'items' => array(
52761 'type' => 'object',
52762 'properties' => array(
52763 'Baz' => array('type' => 'array'),
52764 'Bar' => array(
52765 'type' => 'object',
52766 'properties' => array(
52767 'Baz' => array('type' => 'array'),
52768 )
52769 )
52770 )
52771 )
52772 ));
52773
52774 $value = array();
52775 $visitor->visit($this->command, $this->response, $param, $value);
52776
52777 $value = array(
52778 'Foo' => array(
52779 'Bar' => array()
52780 )
52781 );
52782 $visitor->visit($this->command, $this->response, $param, $value);
52783 $this->assertEquals(array(
52784 'Foo' => array(
52785 array(
52786 'Bar' => array()
52787 )
52788 )
52789 ), $value);
52790 }
52791
52792 /**
52793 * @group issue-399
52794 * @link https://github.com/guzzle/guzzle/issues/399
52795 */
52796 public function testDiscardingUnknownProperties()
52797 {
52798 $visitor = new Visitor();
52799 $param = new Parameter(array(
52800 'name' => 'foo',
52801 'type' => 'object',
52802 'additionalProperties' => false,
52803 'properties' => array(
52804 'bar' => array(
52805 'type' => 'string',
52806 'name' => 'bar',
52807 ),
52808 ),
52809 ));
52810 $this->value = array('foo' => array('bar' => 15, 'unknown' => 'Unknown'));
52811 $visitor->visit($this->command, $this->response, $param, $this->value);
52812 $this->assertEquals(array('foo' => array('bar' => 15)), $this->value);
52813 }
52814
52815 /**
52816 * @group issue-399
52817 * @link https://github.com/guzzle/guzzle/issues/399
52818 */
52819 public function testDiscardingUnknownPropertiesWithAliasing()
52820 {
52821 $visitor = new Visitor();
52822 $param = new Parameter(array(
52823 'name' => 'foo',
52824 'type' => 'object',
52825 'additionalProperties' => false,
52826 'properties' => array(
52827 'bar' => array(
52828 'name' => 'bar',
52829 'sentAs' => 'baz',
52830 ),
52831 ),
52832 ));
52833 $this->value = array('foo' => array('baz' => 15, 'unknown' => 'Unknown'));
52834 $visitor->visit($this->command, $this->response, $param, $this->value);
52835 $this->assertEquals(array('foo' => array('bar' => 15)), $this->value);
52836 }
52837 }
52838 <?php
52839
52840 namespace Guzzle\Tests\Service\Command\LocationVisitor\Response;
52841
52842 use Guzzle\Service\Description\Parameter;
52843 use Guzzle\Http\Message\Response;
52844 use Guzzle\Service\Command\LocationVisitor\Response\StatusCodeVisitor as Visitor;
52845
52846 /**
52847 * @covers Guzzle\Service\Command\LocationVisitor\Response\StatusCodeVisitor
52848 */
52849 class StatusCodeVisitorTest extends AbstractResponseVisitorTest
52850 {
52851 public function testVisitsLocation()
52852 {
52853 $visitor = new Visitor();
52854 $param = new Parameter(array('location' => 'statusCode', 'name' => 'code'));
52855 $visitor->visit($this->command, $this->response, $param, $this->value);
52856 $this->assertEquals(200, $this->value['code']);
52857 }
52858 }
52859 <?php
52860
52861 namespace Guzzle\Tests\Service\Command\LocationVisitor\Response;
52862
52863 use Guzzle\Service\Description\Parameter;
52864 use Guzzle\Http\Message\Response;
52865 use Guzzle\Service\Command\LocationVisitor\Response\ReasonPhraseVisitor as Visitor;
52866
52867 /**
52868 * @covers Guzzle\Service\Command\LocationVisitor\Response\ReasonPhraseVisitor
52869 */
52870 class ReasonPhraseVisitorTest extends AbstractResponseVisitorTest
52871 {
52872 public function testVisitsLocation()
52873 {
52874 $visitor = new Visitor();
52875 $param = new Parameter(array('location' => 'reasonPhrase', 'name' => 'phrase'));
52876 $visitor->visit($this->command, $this->response, $param, $this->value);
52877 $this->assertEquals('OK', $this->value['phrase']);
52878 }
52879 }
52880 <?php
52881
52882 namespace Guzzle\Tests\Service\Command;
52883
52884 use Guzzle\Service\Command\LocationVisitor\VisitorFlyweight;
52885 use Guzzle\Service\Command\LocationVisitor\Request\JsonVisitor as JsonRequestVisitor;
52886 use Guzzle\Service\Command\LocationVisitor\Response\JsonVisitor as JsonResponseVisitor;
52887
52888 /**
52889 * @covers Guzzle\Service\Command\LocationVisitor\VisitorFlyweight
52890 */
52891 class VisitorFlyweightTest extends \Guzzle\Tests\GuzzleTestCase
52892 {
52893 public function testUsesDefaultMappingsWithGetInstance()
52894 {
52895 $f = VisitorFlyweight::getInstance();
52896 $this->assertInstanceOf('Guzzle\Service\Command\LocationVisitor\Request\JsonVisitor', $f->getRequestVisitor('json'));
52897 $this->assertInstanceOf('Guzzle\Service\Command\LocationVisitor\Response\JsonVisitor', $f->getResponseVisitor('json'));
52898 }
52899
52900 public function testCanUseCustomMappings()
52901 {
52902 $f = new VisitorFlyweight(array());
52903 $this->assertEquals(array(), $this->readAttribute($f, 'mappings'));
52904 }
52905
52906 /**
52907 * @expectedException \InvalidArgumentException
52908 * @expectedExceptionMessage No request visitor has been mapped for foo
52909 */
52910 public function testThrowsExceptionWhenRetrievingUnknownVisitor()
52911 {
52912 VisitorFlyweight::getInstance()->getRequestVisitor('foo');
52913 }
52914
52915 public function testCachesVisitors()
52916 {
52917 $f = new VisitorFlyweight();
52918 $v1 = $f->getRequestVisitor('json');
52919 $this->assertSame($v1, $f->getRequestVisitor('json'));
52920 }
52921
52922 public function testAllowsAddingVisitors()
52923 {
52924 $f = new VisitorFlyweight();
52925 $j1 = new JsonRequestVisitor();
52926 $j2 = new JsonResponseVisitor();
52927 $f->addRequestVisitor('json', $j1);
52928 $f->addResponseVisitor('json', $j2);
52929 $this->assertSame($j1, $f->getRequestVisitor('json'));
52930 $this->assertSame($j2, $f->getResponseVisitor('json'));
52931 }
52932 }
52933 <?php
52934
52935 namespace Guzzle\Tests\Service\Command;
52936
52937 use Guzzle\Service\Command\DefaultRequestSerializer;
52938 use Guzzle\Http\Message\EntityEnclosingRequest;
52939 use Guzzle\Service\Client;
52940 use Guzzle\Service\Description\ServiceDescription;
52941 use Guzzle\Service\Description\Operation;
52942 use Guzzle\Service\Description\Parameter;
52943 use Guzzle\Service\Command\LocationVisitor\Request\HeaderVisitor;
52944 use Guzzle\Service\Command\LocationVisitor\VisitorFlyweight;
52945
52946 /**
52947 * @covers Guzzle\Service\Command\DefaultRequestSerializer
52948 */
52949 class DefaultRequestSerializerTest extends \Guzzle\Tests\GuzzleTestCase
52950 {
52951 /** @var EntityEnclosingRequest */
52952 protected $request;
52953
52954 /** @var \Guzzle\Service\Command\AbstractCommand */
52955 protected $command;
52956
52957 /** @var Client */
52958 protected $client;
52959
52960 /** @var DefaultRequestSerializer */
52961 protected $serializer;
52962
52963 /** @var Operation */
52964 protected $operation;
52965
52966 public function setUp()
52967 {
52968 $this->serializer = DefaultRequestSerializer::getInstance();
52969 $this->client = new Client('http://foo.com/baz');
52970 $this->operation = new Operation(array('httpMethod' => 'POST'));
52971 $this->command = $this->getMockBuilder('Guzzle\Service\Command\AbstractCommand')
52972 ->setConstructorArgs(array(array(), $this->operation))
52973 ->getMockForAbstractClass();
52974 $this->command->setClient($this->client);
52975 }
52976
52977 public function testAllowsCustomVisitor()
52978 {
52979 $this->serializer->addVisitor('custom', new HeaderVisitor());
52980 $this->command['test'] = '123';
52981 $this->operation->addParam(new Parameter(array('name' => 'test', 'location' => 'custom')));
52982 $request = $this->serializer->prepare($this->command);
52983 $this->assertEquals('123', (string) $request->getHeader('test'));
52984 }
52985
52986 public function testUsesRelativePath()
52987 {
52988 $this->operation->setUri('bar');
52989 $request = $this->serializer->prepare($this->command);
52990 $this->assertEquals('http://foo.com/baz/bar', (string) $request->getUrl());
52991 }
52992
52993 public function testUsesRelativePathWithUriLocations()
52994 {
52995 $this->command['test'] = '123';
52996 $this->operation->setUri('bar/{test}');
52997 $this->operation->addParam(new Parameter(array('name' => 'test', 'location' => 'uri')));
52998 $request = $this->serializer->prepare($this->command);
52999 $this->assertEquals('http://foo.com/baz/bar/123', (string) $request->getUrl());
53000 }
53001
53002 public function testAllowsCustomFactory()
53003 {
53004 $f = new VisitorFlyweight();
53005 $serializer = new DefaultRequestSerializer($f);
53006 $this->assertSame($f, $this->readAttribute($serializer, 'factory'));
53007 }
53008
53009 public function testMixedParams()
53010 {
53011 $this->operation->setUri('bar{?limit,fields}');
53012 $this->operation->addParam(new Parameter(array(
53013 'name' => 'limit',
53014 'location' => 'uri',
53015 'required' => false,
53016 )));
53017 $this->operation->addParam(new Parameter(array(
53018 'name' => 'fields',
53019 'location' => 'uri',
53020 'required' => true,
53021 )));
53022
53023 $this->command['fields'] = array('id', 'name');
53024
53025 $request = $this->serializer->prepare($this->command);
53026 $this->assertEquals('http://foo.com/baz/bar?fields='.urlencode('id,name'), (string) $request->getUrl());
53027 }
53028
53029 public function testValidatesAdditionalParameters()
53030 {
53031 $description = ServiceDescription::factory(array(
53032 'operations' => array(
53033 'foo' => array(
53034 'httpMethod' => 'PUT',
53035 'parameters' => array(
53036 'bar' => array('location' => 'header')
53037 ),
53038 'additionalParameters' => array(
53039 'location' => 'json'
53040 )
53041 )
53042 )
53043 ));
53044
53045 $client = new Client();
53046 $client->setDescription($description);
53047 $command = $client->getCommand('foo');
53048 $command['bar'] = 'test';
53049 $command['hello'] = 'abc';
53050 $request = $command->prepare();
53051 $this->assertEquals('test', (string) $request->getHeader('bar'));
53052 $this->assertEquals('{"hello":"abc"}', (string) $request->getBody());
53053 }
53054 }
53055 <?php
53056
53057 namespace Guzzle\Tests\Batch;
53058
53059 use Guzzle\Batch\NotifyingBatch;
53060 use Guzzle\Batch\Batch;
53061
53062 /**
53063 * @covers Guzzle\Batch\NotifyingBatch
53064 */
53065 class NotifyingBatchTest extends \Guzzle\Tests\GuzzleTestCase
53066 {
53067 public function testNotifiesAfterFlush()
53068 {
53069 $batch = $this->getMock('Guzzle\Batch\Batch', array('flush'), array(
53070 $this->getMock('Guzzle\Batch\BatchTransferInterface'),
53071 $this->getMock('Guzzle\Batch\BatchDivisorInterface')
53072 ));
53073
53074 $batch->expects($this->once())
53075 ->method('flush')
53076 ->will($this->returnValue(array('foo', 'baz')));
53077
53078 $data = array();
53079 $decorator = new NotifyingBatch($batch, function ($batch) use (&$data) {
53080 $data[] = $batch;
53081 });
53082
53083 $decorator->add('foo')->add('baz');
53084 $decorator->flush();
53085 $this->assertEquals(array(array('foo', 'baz')), $data);
53086 }
53087
53088 /**
53089 * @expectedException Guzzle\Common\Exception\InvalidArgumentException
53090 */
53091 public function testEnsuresCallableIsValid()
53092 {
53093 $batch = new Batch(
53094 $this->getMock('Guzzle\Batch\BatchTransferInterface'),
53095 $this->getMock('Guzzle\Batch\BatchDivisorInterface')
53096 );
53097 $decorator = new NotifyingBatch($batch, 'foo');
53098 }
53099 }
53100 <?php
53101
53102 namespace Guzzle\Tests\Batch;
53103
53104 use Guzzle\Batch\BatchSizeDivisor;
53105
53106 /**
53107 * @covers Guzzle\Batch\BatchSizeDivisor
53108 */
53109 class BatchSizeDivisorTest extends \Guzzle\Tests\GuzzleTestCase
53110 {
53111 public function testDividesBatch()
53112 {
53113 $queue = new \SplQueue();
53114 $queue[] = 'foo';
53115 $queue[] = 'baz';
53116 $queue[] = 'bar';
53117 $d = new BatchSizeDivisor(3);
53118 $this->assertEquals(3, $d->getSize());
53119 $d->setSize(2);
53120 $batches = $d->createBatches($queue);
53121 $this->assertEquals(array(array('foo', 'baz'), array('bar')), $batches);
53122 }
53123 }
53124 <?php
53125
53126 namespace Guzzle\Tests\Batch;
53127
53128 use Guzzle\Batch\BatchCommandTransfer;
53129 use Guzzle\Service\Client;
53130 use Guzzle\Tests\Service\Mock\Command\MockCommand as Mc;
53131
53132 /**
53133 * @covers Guzzle\Batch\BatchCommandTransfer
53134 */
53135 class BatchCommandTransferTest extends \Guzzle\Tests\GuzzleTestCase
53136 {
53137 public function testCreatesBatchesBasedOnClient()
53138 {
53139 $client1 = new Client('http://www.example.com');
53140 $client2 = new Client('http://www.example.com');
53141
53142 $commands = array(new Mc(), new Mc(), new Mc(), new Mc(), new Mc());
53143
53144 $queue = new \SplQueue();
53145 foreach ($commands as $i => $command) {
53146 if ($i % 2) {
53147 $command->setClient($client1);
53148 } else {
53149 $command->setClient($client2);
53150 }
53151 $queue[] = $command;
53152 }
53153
53154 $batch = new BatchCommandTransfer(2);
53155 $this->assertEquals(array(
53156 array($commands[0], $commands[2]),
53157 array($commands[4]),
53158 array($commands[1], $commands[3])
53159 ), $batch->createBatches($queue));
53160 }
53161
53162 /**
53163 * @expectedException Guzzle\Common\Exception\InvalidArgumentException
53164 */
53165 public function testEnsuresAllItemsAreCommands()
53166 {
53167 $queue = new \SplQueue();
53168 $queue[] = 'foo';
53169 $batch = new BatchCommandTransfer(2);
53170 $batch->createBatches($queue);
53171 }
53172
53173 public function testTransfersBatches()
53174 {
53175 $client = $this->getMockBuilder('Guzzle\Service\Client')
53176 ->setMethods(array('send'))
53177 ->getMock();
53178 $client->expects($this->once())
53179 ->method('send');
53180 $command = new Mc();
53181 $command->setClient($client);
53182 $batch = new BatchCommandTransfer(2);
53183 $batch->transfer(array($command));
53184 }
53185
53186 public function testDoesNotTransfersEmptyBatches()
53187 {
53188 $batch = new BatchCommandTransfer(2);
53189 $batch->transfer(array());
53190 }
53191
53192 /**
53193 * @expectedException Guzzle\Service\Exception\InconsistentClientTransferException
53194 */
53195 public function testEnsuresAllCommandsUseTheSameClient()
53196 {
53197 $batch = new BatchCommandTransfer(2);
53198 $client1 = new Client('http://www.example.com');
53199 $client2 = new Client('http://www.example.com');
53200 $command1 = new Mc();
53201 $command1->setClient($client1);
53202 $command2 = new Mc();
53203 $command2->setClient($client2);
53204 $batch->transfer(array($command1, $command2));
53205 }
53206 }
53207 <?php
53208
53209 namespace Guzzle\Tests\Batch;
53210
53211 use Guzzle\Batch\FlushingBatch;
53212 use Guzzle\Batch\Batch;
53213
53214 /**
53215 * @covers Guzzle\Batch\FlushingBatch
53216 */
53217 class FlushingBatchTest extends \Guzzle\Tests\GuzzleTestCase
53218 {
53219 public function testFlushesWhenSizeMeetsThreshold()
53220 {
53221 $t = $this->getMock('Guzzle\Batch\BatchTransferInterface', array('transfer'));
53222 $d = $this->getMock('Guzzle\Batch\BatchDivisorInterface', array('createBatches'));
53223
53224 $batch = new Batch($t, $d);
53225 $queue = $this->readAttribute($batch, 'queue');
53226
53227 $d->expects($this->exactly(2))
53228 ->method('createBatches')
53229 ->will($this->returnCallback(function () use ($queue) {
53230 $items = array();
53231 foreach ($queue as $item) {
53232 $items[] = $item;
53233 }
53234 return array($items);
53235 }));
53236
53237 $t->expects($this->exactly(2))
53238 ->method('transfer');
53239
53240 $flush = new FlushingBatch($batch, 3);
53241 $this->assertEquals(3, $flush->getThreshold());
53242 $flush->setThreshold(2);
53243 $flush->add('foo')->add('baz')->add('bar')->add('bee')->add('boo');
53244 $this->assertEquals(1, count($flush));
53245 }
53246 }
53247 <?php
53248
53249 namespace Guzzle\Tests\Batch;
53250
53251 use Guzzle\Batch\Batch;
53252 use Guzzle\Batch\Exception\BatchTransferException;
53253
53254 /**
53255 * @covers Guzzle\Batch\Batch
53256 */
53257 class BatchTest extends \Guzzle\Tests\GuzzleTestCase
53258 {
53259 private function getMockTransfer()
53260 {
53261 return $this->getMock('Guzzle\Batch\BatchTransferInterface');
53262 }
53263
53264 private function getMockDivisor()
53265 {
53266 return $this->getMock('Guzzle\Batch\BatchDivisorInterface');
53267 }
53268
53269 public function testAddsItemsToQueue()
53270 {
53271 $batch = new Batch($this->getMockTransfer(), $this->getMockDivisor());
53272 $this->assertSame($batch, $batch->add('foo'));
53273 $this->assertEquals(1, count($batch));
53274 }
53275
53276 public function testFlushReturnsItems()
53277 {
53278 $transfer = $this->getMockTransfer();
53279 $transfer->expects($this->exactly(2))
53280 ->method('transfer');
53281
53282 $divisor = $this->getMockDivisor();
53283 $divisor->expects($this->once())
53284 ->method('createBatches')
53285 ->will($this->returnValue(array(array('foo', 'baz'), array('bar'))));
53286
53287 $batch = new Batch($transfer, $divisor);
53288
53289 $batch->add('foo')->add('baz')->add('bar');
53290 $items = $batch->flush();
53291
53292 $this->assertEquals(array('foo', 'baz', 'bar'), $items);
53293 }
53294
53295 public function testThrowsExceptionContainingTheFailedBatch()
53296 {
53297 $called = 0;
53298 $originalException = new \Exception('Foo!');
53299
53300 $transfer = $this->getMockTransfer();
53301 $transfer->expects($this->exactly(2))
53302 ->method('transfer')
53303 ->will($this->returnCallback(function () use (&$called, $originalException) {
53304 if (++$called == 2) {
53305 throw $originalException;
53306 }
53307 }));
53308
53309 $divisor = $this->getMockDivisor();
53310 $batch = new Batch($transfer, $divisor);
53311
53312 // PHPunit clones objects before passing them to a callback.
53313 // Horrible hack to get around this!
53314 $queue = $this->readAttribute($batch, 'queue');
53315
53316 $divisor->expects($this->once())
53317 ->method('createBatches')
53318 ->will($this->returnCallback(function ($batch) use ($queue) {
53319 foreach ($queue as $item) {
53320 $items[] = $item;
53321 }
53322 return array_chunk($items, 2);
53323 }));
53324
53325 $batch->add('foo')->add('baz')->add('bar')->add('bee')->add('boo');
53326 $this->assertFalse($batch->isEmpty());
53327
53328 try {
53329 $items = $batch->flush();
53330 $this->fail('Expected exception');
53331 } catch (BatchTransferException $e) {
53332 $this->assertEquals($originalException, $e->getPrevious());
53333 $this->assertEquals(array('bar', 'bee'), array_values($e->getBatch()));
53334 $this->assertEquals(1, count($batch));
53335 }
53336 }
53337 }
53338 <?php
53339
53340 namespace Guzzle\Tests\Batch;
53341
53342 use Guzzle\Batch\BatchRequestTransfer;
53343 use Guzzle\Http\Client;
53344 use Guzzle\Http\Curl\CurlMulti;
53345
53346 /**
53347 * @covers Guzzle\Batch\BatchRequestTransfer
53348 */
53349 class BatchRequestTransferTest extends \Guzzle\Tests\GuzzleTestCase
53350 {
53351 public function testCreatesBatchesBasedOnCurlMultiHandles()
53352 {
53353 $client1 = new Client('http://www.example.com');
53354 $client1->setCurlMulti(new CurlMulti());
53355
53356 $client2 = new Client('http://www.example.com');
53357 $client2->setCurlMulti(new CurlMulti());
53358
53359 $request1 = $client1->get();
53360 $request2 = $client2->get();
53361 $request3 = $client1->get();
53362 $request4 = $client2->get();
53363 $request5 = $client1->get();
53364
53365 $queue = new \SplQueue();
53366 $queue[] = $request1;
53367 $queue[] = $request2;
53368 $queue[] = $request3;
53369 $queue[] = $request4;
53370 $queue[] = $request5;
53371
53372 $batch = new BatchRequestTransfer(2);
53373 $this->assertEquals(array(
53374 array($request1, $request3),
53375 array($request3),
53376 array($request2, $request4)
53377 ), $batch->createBatches($queue));
53378 }
53379
53380 /**
53381 * @expectedException \Guzzle\Common\Exception\InvalidArgumentException
53382 */
53383 public function testEnsuresAllItemsAreRequests()
53384 {
53385 $queue = new \SplQueue();
53386 $queue[] = 'foo';
53387 $batch = new BatchRequestTransfer(2);
53388 $batch->createBatches($queue);
53389 }
53390
53391 public function testTransfersBatches()
53392 {
53393 $client = new Client('http://localhost:123');
53394 $request = $client->get();
53395 // For some reason... PHP unit clones the request, which emits a request.clone event. This causes the
53396 // 'sorted' property of the event dispatcher to contain an array in the cloned request that is not present in
53397 // the original.
53398 $request->dispatch('request.clone');
53399
53400 $multi = $this->getMock('Guzzle\Http\Curl\CurlMultiInterface');
53401 $client->setCurlMulti($multi);
53402 $multi->expects($this->once())
53403 ->method('add')
53404 ->with($request);
53405 $multi->expects($this->once())
53406 ->method('send');
53407
53408 $batch = new BatchRequestTransfer(2);
53409 $batch->transfer(array($request));
53410 }
53411
53412 public function testDoesNotTransfersEmptyBatches()
53413 {
53414 $batch = new BatchRequestTransfer(2);
53415 $batch->transfer(array());
53416 }
53417 }
53418 <?php
53419
53420 namespace Guzzle\Tests\Batch;
53421
53422 use Guzzle\Batch\Batch;
53423
53424 /**
53425 * @covers Guzzle\Batch\AbstractBatchDecorator
53426 */
53427 class AbstractBatchDecoratorTest extends \Guzzle\Tests\GuzzleTestCase
53428 {
53429 public function testProxiesToWrappedObject()
53430 {
53431 $batch = new Batch(
53432 $this->getMock('Guzzle\Batch\BatchTransferInterface'),
53433 $this->getMock('Guzzle\Batch\BatchDivisorInterface')
53434 );
53435
53436 $decoratorA = $this->getMockBuilder('Guzzle\Batch\AbstractBatchDecorator')
53437 ->setConstructorArgs(array($batch))
53438 ->getMockForAbstractClass();
53439
53440 $decoratorB = $this->getMockBuilder('Guzzle\Batch\AbstractBatchDecorator')
53441 ->setConstructorArgs(array($decoratorA))
53442 ->getMockForAbstractClass();
53443
53444 $decoratorA->add('foo');
53445 $this->assertFalse($decoratorB->isEmpty());
53446 $this->assertFalse($batch->isEmpty());
53447 $this->assertEquals(array($decoratorB, $decoratorA), $decoratorB->getDecorators());
53448 $this->assertEquals(array(), $decoratorB->flush());
53449 }
53450 }
53451 <?php
53452
53453 namespace Guzzle\Tests\Batch;
53454
53455 use Guzzle\Batch\BatchClosureDivisor;
53456
53457 /**
53458 * @covers Guzzle\Batch\BatchClosureDivisor
53459 */
53460 class BatchClosureDivisorTest extends \Guzzle\Tests\GuzzleTestCase
53461 {
53462 /**
53463 * @expectedException Guzzle\Common\Exception\InvalidArgumentException
53464 */
53465 public function testEnsuresCallableIsCallable()
53466 {
53467 $d = new BatchClosureDivisor(new \stdClass());
53468 }
53469
53470 public function testDividesBatch()
53471 {
53472 $queue = new \SplQueue();
53473 $queue[] = 'foo';
53474 $queue[] = 'baz';
53475
53476 $d = new BatchClosureDivisor(function (\SplQueue $queue, $context) {
53477 return array(
53478 array('foo'),
53479 array('baz')
53480 );
53481 }, 'Bar!');
53482
53483 $batches = $d->createBatches($queue);
53484 $this->assertEquals(array(array('foo'), array('baz')), $batches);
53485 }
53486 }
53487 <?php
53488
53489 namespace Guzzle\Tests\Batch;
53490
53491 use Guzzle\Batch\BatchBuilder;
53492
53493 /**
53494 * @covers Guzzle\Batch\BatchBuilder
53495 */
53496 class BatchBuilderTest extends \Guzzle\Tests\GuzzleTestCase
53497 {
53498 private function getMockTransfer()
53499 {
53500 return $this->getMock('Guzzle\Batch\BatchTransferInterface');
53501 }
53502
53503 private function getMockDivisor()
53504 {
53505 return $this->getMock('Guzzle\Batch\BatchDivisorInterface');
53506 }
53507
53508 private function getMockBatchBuilder()
53509 {
53510 return BatchBuilder::factory()
53511 ->transferWith($this->getMockTransfer())
53512 ->createBatchesWith($this->getMockDivisor());
53513 }
53514
53515 public function testFactoryCreatesInstance()
53516 {
53517 $builder = BatchBuilder::factory();
53518 $this->assertInstanceOf('Guzzle\Batch\BatchBuilder', $builder);
53519 }
53520
53521 public function testAddsAutoFlush()
53522 {
53523 $batch = $this->getMockBatchBuilder()->autoFlushAt(10)->build();
53524 $this->assertInstanceOf('Guzzle\Batch\FlushingBatch', $batch);
53525 }
53526
53527 public function testAddsExceptionBuffering()
53528 {
53529 $batch = $this->getMockBatchBuilder()->bufferExceptions()->build();
53530 $this->assertInstanceOf('Guzzle\Batch\ExceptionBufferingBatch', $batch);
53531 }
53532
53533 public function testAddHistory()
53534 {
53535 $batch = $this->getMockBatchBuilder()->keepHistory()->build();
53536 $this->assertInstanceOf('Guzzle\Batch\HistoryBatch', $batch);
53537 }
53538
53539 public function testAddsNotify()
53540 {
53541 $batch = $this->getMockBatchBuilder()->notify(function() {})->build();
53542 $this->assertInstanceOf('Guzzle\Batch\NotifyingBatch', $batch);
53543 }
53544
53545 /**
53546 * @expectedException Guzzle\Common\Exception\RuntimeException
53547 */
53548 public function testTransferStrategyMustBeSet()
53549 {
53550 $batch = BatchBuilder::factory()->createBatchesWith($this->getMockDivisor())->build();
53551 }
53552
53553 /**
53554 * @expectedException Guzzle\Common\Exception\RuntimeException
53555 */
53556 public function testDivisorStrategyMustBeSet()
53557 {
53558 $batch = BatchBuilder::factory()->transferWith($this->getMockTransfer())->build();
53559 }
53560
53561 public function testTransfersRequests()
53562 {
53563 $batch = BatchBuilder::factory()->transferRequests(10)->build();
53564 $this->assertInstanceOf('Guzzle\Batch\BatchRequestTransfer', $this->readAttribute($batch, 'transferStrategy'));
53565 }
53566
53567 public function testTransfersCommands()
53568 {
53569 $batch = BatchBuilder::factory()->transferCommands(10)->build();
53570 $this->assertInstanceOf('Guzzle\Batch\BatchCommandTransfer', $this->readAttribute($batch, 'transferStrategy'));
53571 }
53572 }
53573 <?php
53574
53575 namespace Guzzle\Tests\Batch;
53576
53577 use Guzzle\Batch\HistoryBatch;
53578 use Guzzle\Batch\Batch;
53579
53580 /**
53581 * @covers Guzzle\Batch\HistoryBatch
53582 */
53583 class HistoryBatchTest extends \Guzzle\Tests\GuzzleTestCase
53584 {
53585 public function testMaintainsHistoryOfItemsAddedToBatch()
53586 {
53587 $batch = new Batch(
53588 $this->getMock('Guzzle\Batch\BatchTransferInterface'),
53589 $this->getMock('Guzzle\Batch\BatchDivisorInterface')
53590 );
53591
53592 $history = new HistoryBatch($batch);
53593 $history->add('foo')->add('baz');
53594 $this->assertEquals(array('foo', 'baz'), $history->getHistory());
53595 $history->clearHistory();
53596 $this->assertEquals(array(), $history->getHistory());
53597 }
53598 }
53599 <?php
53600
53601 namespace Guzzle\Tests\Batch;
53602
53603 use Guzzle\Batch\BatchClosureTransfer;
53604
53605 /**
53606 * @covers Guzzle\Batch\BatchClosureTransfer
53607 */
53608 class BatchClosureTransferTest extends \Guzzle\Tests\GuzzleTestCase
53609 {
53610 /** @var \Guzzle\Batch\BatchClosureTransfer The transfer fixture */
53611 protected $transferStrategy;
53612
53613 /** @var array|null An array for keeping track of items passed into the transfer closure */
53614 protected $itemsTransferred;
53615
53616 protected function setUp()
53617 {
53618 $this->itemsTransferred = null;
53619 $itemsTransferred =& $this->itemsTransferred;
53620
53621 $this->transferStrategy = new BatchClosureTransfer(function (array $batch) use (&$itemsTransferred) {
53622 $itemsTransferred = $batch;
53623 return;
53624 });
53625 }
53626
53627 public function testTransfersBatch()
53628 {
53629 $batchedItems = array('foo', 'bar', 'baz');
53630 $this->transferStrategy->transfer($batchedItems);
53631
53632 $this->assertEquals($batchedItems, $this->itemsTransferred);
53633 }
53634
53635 public function testTransferBailsOnEmptyBatch()
53636 {
53637 $batchedItems = array();
53638 $this->transferStrategy->transfer($batchedItems);
53639
53640 $this->assertNull($this->itemsTransferred);
53641 }
53642
53643 /**
53644 * @expectedException Guzzle\Common\Exception\InvalidArgumentException
53645 */
53646 public function testEnsuresCallableIsCallable()
53647 {
53648 $foo = new BatchClosureTransfer('uh oh!');
53649 }
53650 }
53651 <?php
53652
53653 namespace Guzzle\Tests\Batch;
53654
53655 use Guzzle\Batch\ExceptionBufferingBatch;
53656 use Guzzle\Batch\Batch;
53657 use Guzzle\Batch\BatchSizeDivisor;
53658
53659 /**
53660 * @covers Guzzle\Batch\ExceptionBufferingBatch
53661 */
53662 class ExceptionBufferingBatchTest extends \Guzzle\Tests\GuzzleTestCase
53663 {
53664 public function testFlushesEntireBatchWhileBufferingErroredBatches()
53665 {
53666 $t = $this->getMockBuilder('Guzzle\Batch\BatchTransferInterface')
53667 ->setMethods(array('transfer'))
53668 ->getMock();
53669
53670 $d = new BatchSizeDivisor(1);
53671 $batch = new Batch($t, $d);
53672
53673 $called = 0;
53674 $t->expects($this->exactly(3))
53675 ->method('transfer')
53676 ->will($this->returnCallback(function ($batch) use (&$called) {
53677 if (++$called === 2) {
53678 throw new \Exception('Foo');
53679 }
53680 }));
53681
53682 $decorator = new ExceptionBufferingBatch($batch);
53683 $decorator->add('foo')->add('baz')->add('bar');
53684 $result = $decorator->flush();
53685
53686 $e = $decorator->getExceptions();
53687 $this->assertEquals(1, count($e));
53688 $this->assertEquals(array('baz'), $e[0]->getBatch());
53689
53690 $decorator->clearExceptions();
53691 $this->assertEquals(0, count($decorator->getExceptions()));
53692
53693 $this->assertEquals(array('foo', 'bar'), $result);
53694 }
53695 }
53696 <?php
53697
53698 namespace Guzzle\Tests\Parser;
53699
53700 use Guzzle\Parser\ParserRegistry;
53701
53702 /**
53703 * @covers Guzzle\Parser\ParserRegistry
53704 */
53705 class ParserRegistryTest extends \Guzzle\Tests\GuzzleTestCase
53706 {
53707 public function testStoresObjects()
53708 {
53709 $r = new ParserRegistry();
53710 $c = new \stdClass();
53711 $r->registerParser('foo', $c);
53712 $this->assertSame($c, $r->getParser('foo'));
53713 }
53714
53715 public function testReturnsNullWhenNotFound()
53716 {
53717 $r = new ParserRegistry();
53718 $this->assertNull($r->getParser('FOO'));
53719 }
53720
53721 public function testReturnsLazyLoadedDefault()
53722 {
53723 $r = new ParserRegistry();
53724 $c = $r->getParser('cookie');
53725 $this->assertInstanceOf('Guzzle\Parser\Cookie\CookieParser', $c);
53726 $this->assertSame($c, $r->getParser('cookie'));
53727 }
53728 }
53729 <?php
53730
53731 namespace Guzzle\Tests\Parsers\UriTemplate;
53732
53733 use Guzzle\Parser\UriTemplate\PeclUriTemplate;
53734
53735 /**
53736 * @covers Guzzle\Parser\UriTemplate\PeclUriTemplate
53737 */
53738 class PeclUriTemplateTest extends AbstractUriTemplateTest
53739 {
53740 protected function setUp()
53741 {
53742 if (!extension_loaded('uri_template')) {
53743 $this->markTestSkipped('uri_template PECL extension must be installed to test PeclUriTemplate');
53744 }
53745 }
53746
53747 /**
53748 * @dataProvider templateProvider
53749 */
53750 public function testExpandsUriTemplates($template, $expansion, $params)
53751 {
53752 $uri = new PeclUriTemplate($template);
53753 $this->assertEquals($expansion, $uri->expand($template, $params));
53754 }
53755 }
53756 <?php
53757
53758 namespace Guzzle\Tests\Parsers\UriTemplate;
53759
53760 use Guzzle\Parser\UriTemplate\UriTemplate;
53761
53762 /**
53763 * @covers Guzzle\Parser\UriTemplate\UriTemplate
53764 */
53765 class UriTemplateTest extends AbstractUriTemplateTest
53766 {
53767 /**
53768 * @dataProvider templateProvider
53769 */
53770 public function testExpandsUriTemplates($template, $expansion, $params)
53771 {
53772 $uri = new UriTemplate($template);
53773 $this->assertEquals($expansion, $uri->expand($template, $params));
53774 }
53775
53776 public function expressionProvider()
53777 {
53778 return array(
53779 array(
53780 '{+var*}', array(
53781 'operator' => '+',
53782 'values' => array(
53783 array('value' => 'var', 'modifier' => '*')
53784 )
53785 ),
53786 ),
53787 array(
53788 '{?keys,var,val}', array(
53789 'operator' => '?',
53790 'values' => array(
53791 array('value' => 'keys', 'modifier' => ''),
53792 array('value' => 'var', 'modifier' => ''),
53793 array('value' => 'val', 'modifier' => '')
53794 )
53795 ),
53796 ),
53797 array(
53798 '{+x,hello,y}', array(
53799 'operator' => '+',
53800 'values' => array(
53801 array('value' => 'x', 'modifier' => ''),
53802 array('value' => 'hello', 'modifier' => ''),
53803 array('value' => 'y', 'modifier' => '')
53804 )
53805 )
53806 )
53807 );
53808 }
53809
53810 /**
53811 * @dataProvider expressionProvider
53812 */
53813 public function testParsesExpressions($exp, $data)
53814 {
53815 $template = new UriTemplate($exp);
53816
53817 // Access the config object
53818 $class = new \ReflectionClass($template);
53819 $method = $class->getMethod('parseExpression');
53820 $method->setAccessible(true);
53821
53822 $exp = substr($exp, 1, -1);
53823 $this->assertEquals($data, $method->invokeArgs($template, array($exp)));
53824 }
53825
53826 /**
53827 * @ticket https://github.com/guzzle/guzzle/issues/90
53828 */
53829 public function testAllowsNestedArrayExpansion()
53830 {
53831 $template = new UriTemplate();
53832
53833 $result = $template->expand('http://example.com{+path}{/segments}{?query,data*,foo*}', array(
53834 'path' => '/foo/bar',
53835 'segments' => array('one', 'two'),
53836 'query' => 'test',
53837 'data' => array(
53838 'more' => array('fun', 'ice cream')
53839 ),
53840 'foo' => array(
53841 'baz' => array(
53842 'bar' => 'fizz',
53843 'test' => 'buzz'
53844 ),
53845 'bam' => 'boo'
53846 )
53847 ));
53848
53849 $this->assertEquals('http://example.com/foo/bar/one,two?query=test&more%5B0%5D=fun&more%5B1%5D=ice%20cream&baz%5Bbar%5D=fizz&baz%5Btest%5D=buzz&bam=boo', $result);
53850 }
53851 }
53852 <?php
53853
53854 namespace Guzzle\Tests\Parsers\UriTemplate;
53855
53856 abstract class AbstractUriTemplateTest extends \Guzzle\Tests\GuzzleTestCase
53857 {
53858 /**
53859 * @return array
53860 */
53861 public function templateProvider()
53862 {
53863 $t = array();
53864 $params = array(
53865 'var' => 'value',
53866 'hello' => 'Hello World!',
53867 'empty' => '',
53868 'path' => '/foo/bar',
53869 'x' => '1024',
53870 'y' => '768',
53871 'null' => null,
53872 'list' => array('red', 'green', 'blue'),
53873 'keys' => array(
53874 "semi" => ';',
53875 "dot" => '.',
53876 "comma" => ','
53877 ),
53878 'empty_keys' => array(),
53879 );
53880
53881 return array_map(function($t) use ($params) {
53882 $t[] = $params;
53883 return $t;
53884 }, array(
53885 array('foo', 'foo'),
53886 array('{var}', 'value'),
53887 array('{hello}', 'Hello%20World%21'),
53888 array('{+var}', 'value'),
53889 array('{+hello}', 'Hello%20World!'),
53890 array('{+path}/here', '/foo/bar/here'),
53891 array('here?ref={+path}', 'here?ref=/foo/bar'),
53892 array('X{#var}', 'X#value'),
53893 array('X{#hello}', 'X#Hello%20World!'),
53894 array('map?{x,y}', 'map?1024,768'),
53895 array('{x,hello,y}', '1024,Hello%20World%21,768'),
53896 array('{+x,hello,y}', '1024,Hello%20World!,768'),
53897 array('{+path,x}/here', '/foo/bar,1024/here'),
53898 array('{#x,hello,y}', '#1024,Hello%20World!,768'),
53899 array('{#path,x}/here', '#/foo/bar,1024/here'),
53900 array('X{.var}', 'X.value'),
53901 array('X{.x,y}', 'X.1024.768'),
53902 array('{/var}', '/value'),
53903 array('{/var,x}/here', '/value/1024/here'),
53904 array('{;x,y}', ';x=1024;y=768'),
53905 array('{;x,y,empty}', ';x=1024;y=768;empty'),
53906 array('{?x,y}', '?x=1024&y=768'),
53907 array('{?x,y,empty}', '?x=1024&y=768&empty='),
53908 array('?fixed=yes{&x}', '?fixed=yes&x=1024'),
53909 array('{&x,y,empty}', '&x=1024&y=768&empty='),
53910 array('{var:3}', 'val'),
53911 array('{var:30}', 'value'),
53912 array('{list}', 'red,green,blue'),
53913 array('{list*}', 'red,green,blue'),
53914 array('{keys}', 'semi,%3B,dot,.,comma,%2C'),
53915 array('{keys*}', 'semi=%3B,dot=.,comma=%2C'),
53916 array('{+path:6}/here', '/foo/b/here'),
53917 array('{+list}', 'red,green,blue'),
53918 array('{+list*}', 'red,green,blue'),
53919 array('{+keys}', 'semi,;,dot,.,comma,,'),
53920 array('{+keys*}', 'semi=;,dot=.,comma=,'),
53921 array('{#path:6}/here', '#/foo/b/here'),
53922 array('{#list}', '#red,green,blue'),
53923 array('{#list*}', '#red,green,blue'),
53924 array('{#keys}', '#semi,;,dot,.,comma,,'),
53925 array('{#keys*}', '#semi=;,dot=.,comma=,'),
53926 array('X{.var:3}', 'X.val'),
53927 array('X{.list}', 'X.red,green,blue'),
53928 array('X{.list*}', 'X.red.green.blue'),
53929 array('X{.keys}', 'X.semi,%3B,dot,.,comma,%2C'),
53930 array('X{.keys*}', 'X.semi=%3B.dot=..comma=%2C'),
53931 array('{/var:1,var}', '/v/value'),
53932 array('{/list}', '/red,green,blue'),
53933 array('{/list*}', '/red/green/blue'),
53934 array('{/list*,path:4}', '/red/green/blue/%2Ffoo'),
53935 array('{/keys}', '/semi,%3B,dot,.,comma,%2C'),
53936 array('{/keys*}', '/semi=%3B/dot=./comma=%2C'),
53937 array('{;hello:5}', ';hello=Hello'),
53938 array('{;list}', ';list=red,green,blue'),
53939 array('{;list*}', ';list=red;list=green;list=blue'),
53940 array('{;keys}', ';keys=semi,%3B,dot,.,comma,%2C'),
53941 array('{;keys*}', ';semi=%3B;dot=.;comma=%2C'),
53942 array('{?var:3}', '?var=val'),
53943 array('{?list}', '?list=red,green,blue'),
53944 array('{?list*}', '?list=red&list=green&list=blue'),
53945 array('{?keys}', '?keys=semi,%3B,dot,.,comma,%2C'),
53946 array('{?keys*}', '?semi=%3B&dot=.&comma=%2C'),
53947 array('{&var:3}', '&var=val'),
53948 array('{&list}', '&list=red,green,blue'),
53949 array('{&list*}', '&list=red&list=green&list=blue'),
53950 array('{&keys}', '&keys=semi,%3B,dot,.,comma,%2C'),
53951 array('{&keys*}', '&semi=%3B&dot=.&comma=%2C'),
53952 array('{.null}', ''),
53953 array('{.null,var}', '.value'),
53954 array('X{.empty_keys*}', 'X'),
53955 array('X{.empty_keys}', 'X'),
53956 // Test that missing expansions are skipped
53957 array('test{&missing*}', 'test'),
53958 // Test that multiple expansions can be set
53959 array('http://{var}/{var:2}{?keys*}', 'http://value/va?semi=%3B&dot=.&comma=%2C'),
53960 // Test more complex query string stuff
53961 array('http://www.test.com{+path}{?var,keys*}', 'http://www.test.com/foo/bar?var=value&semi=%3B&dot=.&comma=%2C')
53962 ));
53963 }
53964 }
53965 <?php
53966
53967 namespace Guzzle\Tests\Parser\Cookie;
53968
53969 use Guzzle\Parser\Cookie\CookieParser;
53970
53971 /**
53972 * @covers Guzzle\Parser\Cookie\CookieParser
53973 */
53974 class CookieParserTest extends CookieParserProvider
53975 {
53976 protected $cookieParserClass = 'Guzzle\Parser\Cookie\CookieParser';
53977
53978 public function testUrlDecodesCookies()
53979 {
53980 $parser = new CookieParser();
53981 $result = $parser->parseCookie('foo=baz+bar', null, null, true);
53982 $this->assertEquals(array(
53983 'foo' => 'baz bar'
53984 ), $result['cookies']);
53985 }
53986 }
53987 <?php
53988
53989 namespace Guzzle\Tests\Parser\Cookie;
53990
53991 use Guzzle\Http\Url;
53992
53993 /**
53994 * @covers Guzzle\Parser\Cookie\CookieParser
53995 */
53996 class CookieParserProvider extends \Guzzle\Tests\GuzzleTestCase
53997 {
53998 /**
53999 * Provides the parsed information from a cookie
54000 *
54001 * @return array
54002 */
54003 public function cookieParserDataProvider()
54004 {
54005 return array(
54006 array(
54007 'ASIHTTPRequestTestCookie=This+is+the+value; expires=Sat, 26-Jul-2008 17:00:42 GMT; path=/tests; domain=allseeing-i.com; PHPSESSID=6c951590e7a9359bcedde25cda73e43c; path=/";',
54008 array(
54009 'domain' => 'allseeing-i.com',
54010 'path' => '/',
54011 'data' => array(
54012 'PHPSESSID' => '6c951590e7a9359bcedde25cda73e43c'
54013 ),
54014 'max_age' => NULL,
54015 'expires' => 'Sat, 26-Jul-2008 17:00:42 GMT',
54016 'version' => NULL,
54017 'secure' => NULL,
54018 'discard' => NULL,
54019 'port' => NULL,
54020 'cookies' => array(
54021 'ASIHTTPRequestTestCookie' => 'This+is+the+value'
54022 ),
54023 'comment' => null,
54024 'comment_url' => null,
54025 'http_only' => false
54026 )
54027 ),
54028 array('', false),
54029 array('foo', false),
54030 // Test setting a blank value for a cookie
54031 array(array(
54032 'foo=', 'foo =', 'foo =;', 'foo= ;', 'foo =', 'foo= '),
54033 array(
54034 'cookies' => array(
54035 'foo' => ''
54036 ),
54037 'data' => array(),
54038 'discard' => null,
54039 'domain' => null,
54040 'expires' => null,
54041 'max_age' => null,
54042 'path' => '/',
54043 'port' => null,
54044 'secure' => null,
54045 'version' => null,
54046 'comment' => null,
54047 'comment_url' => null,
54048 'http_only' => false
54049 )
54050 ),
54051 // Test setting a value and removing quotes
54052 array(array(
54053 'foo=1', 'foo =1', 'foo =1;', 'foo=1 ;', 'foo =1', 'foo= 1', 'foo = 1 ;', 'foo="1"', 'foo="1";', 'foo= "1";'),
54054 array(
54055 'cookies' => array(
54056 'foo' => '1'
54057 ),
54058 'data' => array(),
54059 'discard' => null,
54060 'domain' => null,
54061 'expires' => null,
54062 'max_age' => null,
54063 'path' => '/',
54064 'port' => null,
54065 'secure' => null,
54066 'version' => null,
54067 'comment' => null,
54068 'comment_url' => null,
54069 'http_only' => false
54070 )
54071 ),
54072 // Test setting multiple values
54073 array(array(
54074 'foo=1; bar=2;', 'foo =1; bar = "2"', 'foo=1; bar=2'),
54075 array(
54076 'cookies' => array(
54077 'foo' => '1',
54078 'bar' => '2',
54079 ),
54080 'data' => array(),
54081 'discard' => null,
54082 'domain' => null,
54083 'expires' => null,
54084 'max_age' => null,
54085 'path' => '/',
54086 'port' => null,
54087 'secure' => null,
54088 'version' => null,
54089 'comment' => null,
54090 'comment_url' => null,
54091 'http_only' => false
54092 )
54093 ),
54094 // Tests getting the domain and path from a reference request
54095 array(array(
54096 'foo=1; port="80,8081"; httponly', 'foo=1; port="80,8081"; domain=www.test.com; HttpOnly;', 'foo=1; ; domain=www.test.com; path=/path/; port="80,8081"; HttpOnly;'),
54097 array(
54098 'cookies' => array(
54099 'foo' => 1
54100 ),
54101 'data' => array(),
54102 'discard' => null,
54103 'domain' => 'www.test.com',
54104 'expires' => null,
54105 'max_age' => null,
54106 'path' => '/path/',
54107 'port' => array('80', '8081'),
54108 'secure' => null,
54109 'version' => null,
54110 'comment' => null,
54111 'comment_url' => null,
54112 'http_only' => true
54113 ),
54114 'http://www.test.com/path/'
54115 ),
54116 // Some of the following tests are based on http://framework.zend.com/svn/framework/standard/trunk/tests/Zend/Http/CookieTest.php
54117 array(
54118 'justacookie=foo; domain=example.com',
54119 array(
54120 'cookies' => array(
54121 'justacookie' => 'foo'
54122 ),
54123 'domain' => 'example.com',
54124 'path' => '',
54125 'data' => array(),
54126 'discard' => null,
54127 'expires' => null,
54128 'max_age' => null,
54129 'path' => '/',
54130 'port' => null,
54131 'secure' => null,
54132 'version' => null,
54133 'comment' => null,
54134 'comment_url' => null,
54135 'http_only' => false
54136 )
54137 ),
54138 array(
54139 'expires=tomorrow; secure; path=/Space Out/; expires=Tue, 21-Nov-2006 08:33:44 GMT; domain=.example.com',
54140 array(
54141 'cookies' => array(
54142 'expires' => 'tomorrow'
54143 ),
54144 'domain' => '.example.com',
54145 'path' => '/Space Out/',
54146 'expires' => 'Tue, 21-Nov-2006 08:33:44 GMT',
54147 'data' => array(),
54148 'discard' => null,
54149 'port' => null,
54150 'secure' => true,
54151 'version' => null,
54152 'max_age' => null,
54153 'comment' => null,
54154 'comment_url' => null,
54155 'http_only' => false
54156 )
54157 ),
54158 array(
54159 'domain=unittests; expires=Tue, 21-Nov-2006 08:33:44 GMT; domain=example.com; path=/some value/',
54160 array(
54161 'cookies' => array(
54162 'domain' => 'unittests'
54163 ),
54164 'domain' => 'example.com',
54165 'path' => '/some value/',
54166 'expires' => 'Tue, 21-Nov-2006 08:33:44 GMT',
54167 'secure' => false,
54168 'data' => array(),
54169 'discard' => null,
54170 'max_age' => null,
54171 'port' => null,
54172 'version' => null,
54173 'comment' => null,
54174 'comment_url' => null,
54175 'http_only' => false
54176 )
54177 ),
54178 array(
54179 'path=indexAction; path=/; domain=.foo.com; expires=Tue, 21-Nov-2006 08:33:44 GMT',
54180 array(
54181 'cookies' => array(
54182 'path' => 'indexAction'
54183 ),
54184 'domain' => '.foo.com',
54185 'path' => '/',
54186 'expires' => 'Tue, 21-Nov-2006 08:33:44 GMT',
54187 'secure' => false,
54188 'data' => array(),
54189 'discard' => null,
54190 'max_age' => null,
54191 'port' => null,
54192 'version' => null,
54193 'comment' => null,
54194 'comment_url' => null,
54195 'http_only' => false
54196 )
54197 ),
54198 array(
54199 'secure=sha1; secure; SECURE; domain=some.really.deep.domain.com; version=1; Max-Age=86400',
54200 array(
54201 'cookies' => array(
54202 'secure' => 'sha1'
54203 ),
54204 'domain' => 'some.really.deep.domain.com',
54205 'path' => '/',
54206 'secure' => true,
54207 'data' => array(),
54208 'discard' => null,
54209 'expires' => time() + 86400,
54210 'max_age' => 86400,
54211 'port' => null,
54212 'version' => 1,
54213 'comment' => null,
54214 'comment_url' => null,
54215 'http_only' => false
54216 )
54217 ),
54218 array(
54219 'PHPSESSID=123456789+abcd%2Cef; secure; discard; domain=.localdomain; path=/foo/baz; expires=Tue, 21-Nov-2006 08:33:44 GMT;',
54220 array(
54221 'cookies' => array(
54222 'PHPSESSID' => '123456789+abcd%2Cef'
54223 ),
54224 'domain' => '.localdomain',
54225 'path' => '/foo/baz',
54226 'expires' => 'Tue, 21-Nov-2006 08:33:44 GMT',
54227 'secure' => true,
54228 'data' => array(),
54229 'discard' => true,
54230 'max_age' => null,
54231 'port' => null,
54232 'version' => null,
54233 'comment' => null,
54234 'comment_url' => null,
54235 'http_only' => false
54236 )
54237 ),
54238 );
54239 }
54240
54241 /**
54242 * @dataProvider cookieParserDataProvider
54243 */
54244 public function testParseCookie($cookie, $parsed, $url = null)
54245 {
54246 $c = $this->cookieParserClass;
54247 $parser = new $c();
54248
54249 $request = null;
54250 if ($url) {
54251 $url = Url::factory($url);
54252 $host = $url->getHost();
54253 $path = $url->getPath();
54254 } else {
54255 $host = '';
54256 $path = '';
54257 }
54258
54259 foreach ((array) $cookie as $c) {
54260 $p = $parser->parseCookie($c, $host, $path);
54261
54262 // Remove expires values from the assertion if they are relatively equal
54263 if ($p['expires'] != $parsed['expires']) {
54264 if (abs($p['expires'] - $parsed['expires']) < 20) {
54265 unset($p['expires']);
54266 unset($parsed['expires']);
54267 }
54268 }
54269
54270 if (is_array($parsed)) {
54271 foreach ($parsed as $key => $value) {
54272 $this->assertEquals($parsed[$key], $p[$key], 'Comparing ' . $key . ' ' . var_export($value, true) . ' : ' . var_export($parsed, true) . ' | ' . var_export($p, true));
54273 }
54274
54275 foreach ($p as $key => $value) {
54276 $this->assertEquals($p[$key], $parsed[$key], 'Comparing ' . $key . ' ' . var_export($value, true) . ' : ' . var_export($parsed, true) . ' | ' . var_export($p, true));
54277 }
54278 } else {
54279 $this->assertEquals($parsed, $p);
54280 }
54281 }
54282 }
54283 }
54284 <?php
54285
54286 namespace Guzzle\Tests\Parser\Message;
54287
54288 use Guzzle\Parser\Message\MessageParser;
54289
54290 /**
54291 * @covers Guzzle\Parser\Message\AbstractMessageParser
54292 * @covers Guzzle\Parser\Message\MessageParser
54293 */
54294 class MessageParserTest extends MessageParserProvider
54295 {
54296 /**
54297 * @dataProvider requestProvider
54298 */
54299 public function testParsesRequests($message, $parts)
54300 {
54301 $parser = new MessageParser();
54302 $this->compareRequestResults($parts, $parser->parseRequest($message));
54303 }
54304
54305 /**
54306 * @dataProvider responseProvider
54307 */
54308 public function testParsesResponses($message, $parts)
54309 {
54310 $parser = new MessageParser();
54311 $this->compareResponseResults($parts, $parser->parseResponse($message));
54312 }
54313
54314 public function testParsesRequestsWithMissingProtocol()
54315 {
54316 $parser = new MessageParser();
54317 $parts = $parser->parseRequest("GET /\r\nHost: Foo.com\r\n\r\n");
54318 $this->assertEquals('GET', $parts['method']);
54319 $this->assertEquals('HTTP', $parts['protocol']);
54320 $this->assertEquals('1.1', $parts['version']);
54321 }
54322
54323 public function testParsesRequestsWithMissingVersion()
54324 {
54325 $parser = new MessageParser();
54326 $parts = $parser->parseRequest("GET / HTTP\r\nHost: Foo.com\r\n\r\n");
54327 $this->assertEquals('GET', $parts['method']);
54328 $this->assertEquals('HTTP', $parts['protocol']);
54329 $this->assertEquals('1.1', $parts['version']);
54330 }
54331
54332 public function testParsesResponsesWithMissingReasonPhrase()
54333 {
54334 $parser = new MessageParser();
54335 $parts = $parser->parseResponse("HTTP/1.1 200\r\n\r\n");
54336 $this->assertEquals('200', $parts['code']);
54337 $this->assertEquals('', $parts['reason_phrase']);
54338 $this->assertEquals('HTTP', $parts['protocol']);
54339 $this->assertEquals('1.1', $parts['version']);
54340 }
54341 }
54342 <?php
54343
54344 namespace Guzzle\Tests\Parser\Message;
54345
54346 class MessageParserProvider extends \Guzzle\Tests\GuzzleTestCase
54347 {
54348 public function requestProvider()
54349 {
54350 $auth = base64_encode('michael:foo');
54351
54352 return array(
54353
54354 // Empty request
54355 array('', false),
54356
54357 // Converts casing of request. Does not require host header.
54358 array("GET / HTTP/1.1\r\n\r\n", array(
54359 'method' => 'GET',
54360 'protocol' => 'HTTP',
54361 'version' => '1.1',
54362 'request_url' => array(
54363 'scheme' => 'http',
54364 'host' => '',
54365 'port' => '',
54366 'path' => '/',
54367 'query' => ''
54368 ),
54369 'headers' => array(),
54370 'body' => ''
54371 )),
54372 // Path and query string, multiple header values per header and case sensitive storage
54373 array("HEAD /path?query=foo HTTP/1.0\r\nHost: example.com\r\nX-Foo: foo\r\nx-foo: Bar\r\nX-Foo: foo\r\nX-Foo: Baz\r\n\r\n", array(
54374 'method' => 'HEAD',
54375 'protocol' => 'HTTP',
54376 'version' => '1.0',
54377 'request_url' => array(
54378 'scheme' => 'http',
54379 'host' => 'example.com',
54380 'port' => '',
54381 'path' => '/path',
54382 'query' => 'query=foo'
54383 ),
54384 'headers' => array(
54385 'Host' => 'example.com',
54386 'X-Foo' => array('foo', 'foo', 'Baz'),
54387 'x-foo' => 'Bar'
54388 ),
54389 'body' => ''
54390 )),
54391 // Includes a body
54392 array("PUT / HTTP/1.0\r\nhost: example.com:443\r\nContent-Length: 4\r\n\r\ntest", array(
54393 'method' => 'PUT',
54394 'protocol' => 'HTTP',
54395 'version' => '1.0',
54396 'request_url' => array(
54397 'scheme' => 'https',
54398 'host' => 'example.com',
54399 'port' => '443',
54400 'path' => '/',
54401 'query' => ''
54402 ),
54403 'headers' => array(
54404 'host' => 'example.com:443',
54405 'Content-Length' => '4'
54406 ),
54407 'body' => 'test'
54408 )),
54409 // Includes Authorization headers
54410 array("GET / HTTP/1.1\r\nHost: example.com:8080\r\nAuthorization: Basic {$auth}\r\n\r\n", array(
54411 'method' => 'GET',
54412 'protocol' => 'HTTP',
54413 'version' => '1.1',
54414 'request_url' => array(
54415 'scheme' => 'http',
54416 'host' => 'example.com',
54417 'port' => '8080',
54418 'path' => '/',
54419 'query' => ''
54420 ),
54421 'headers' => array(
54422 'Host' => 'example.com:8080',
54423 'Authorization' => "Basic {$auth}"
54424 ),
54425 'body' => ''
54426 )),
54427 // Include authorization header
54428 array("GET / HTTP/1.1\r\nHost: example.com:8080\r\nauthorization: Basic {$auth}\r\n\r\n", array(
54429 'method' => 'GET',
54430 'protocol' => 'HTTP',
54431 'version' => '1.1',
54432 'request_url' => array(
54433 'scheme' => 'http',
54434 'host' => 'example.com',
54435 'port' => '8080',
54436 'path' => '/',
54437 'query' => ''
54438 ),
54439 'headers' => array(
54440 'Host' => 'example.com:8080',
54441 'authorization' => "Basic {$auth}"
54442 ),
54443 'body' => ''
54444 )),
54445 );
54446 }
54447
54448 public function responseProvider()
54449 {
54450 return array(
54451 // Empty request
54452 array('', false),
54453
54454 array("HTTP/1.1 200 OK\r\nContent-Length: 0\r\n\r\n", array(
54455 'protocol' => 'HTTP',
54456 'version' => '1.1',
54457 'code' => '200',
54458 'reason_phrase' => 'OK',
54459 'headers' => array(
54460 'Content-Length' => 0
54461 ),
54462 'body' => ''
54463 )),
54464 array("HTTP/1.0 400 Bad Request\r\nContent-Length: 0\r\n\r\n", array(
54465 'protocol' => 'HTTP',
54466 'version' => '1.0',
54467 'code' => '400',
54468 'reason_phrase' => 'Bad Request',
54469 'headers' => array(
54470 'Content-Length' => 0
54471 ),
54472 'body' => ''
54473 )),
54474 array("HTTP/1.0 100 Continue\r\n\r\n", array(
54475 'protocol' => 'HTTP',
54476 'version' => '1.0',
54477 'code' => '100',
54478 'reason_phrase' => 'Continue',
54479 'headers' => array(),
54480 'body' => ''
54481 )),
54482 array("HTTP/1.1 204 No Content\r\nX-Foo: foo\r\nx-foo: Bar\r\nX-Foo: foo\r\n\r\n", array(
54483 'protocol' => 'HTTP',
54484 'version' => '1.1',
54485 'code' => '204',
54486 'reason_phrase' => 'No Content',
54487 'headers' => array(
54488 'X-Foo' => array('foo', 'foo'),
54489 'x-foo' => 'Bar'
54490 ),
54491 'body' => ''
54492 )),
54493 array("HTTP/1.1 200 Ok that is great!\r\nContent-Length: 4\r\n\r\nTest", array(
54494 'protocol' => 'HTTP',
54495 'version' => '1.1',
54496 'code' => '200',
54497 'reason_phrase' => 'Ok that is great!',
54498 'headers' => array(
54499 'Content-Length' => 4
54500 ),
54501 'body' => 'Test'
54502 )),
54503 );
54504 }
54505
54506 public function compareRequestResults($result, $expected)
54507 {
54508 if (!$result) {
54509 $this->assertFalse($expected);
54510 return;
54511 }
54512
54513 $this->assertEquals($result['method'], $expected['method']);
54514 $this->assertEquals($result['protocol'], $expected['protocol']);
54515 $this->assertEquals($result['version'], $expected['version']);
54516 $this->assertEquals($result['request_url'], $expected['request_url']);
54517 $this->assertEquals($result['body'], $expected['body']);
54518 $this->compareHttpHeaders($result['headers'], $expected['headers']);
54519 }
54520
54521 public function compareResponseResults($result, $expected)
54522 {
54523 if (!$result) {
54524 $this->assertFalse($expected);
54525 return;
54526 }
54527
54528 $this->assertEquals($result['protocol'], $expected['protocol']);
54529 $this->assertEquals($result['version'], $expected['version']);
54530 $this->assertEquals($result['code'], $expected['code']);
54531 $this->assertEquals($result['reason_phrase'], $expected['reason_phrase']);
54532 $this->assertEquals($result['body'], $expected['body']);
54533 $this->compareHttpHeaders($result['headers'], $expected['headers']);
54534 }
54535
54536 protected function normalizeHeaders($headers)
54537 {
54538 $normalized = array();
54539 foreach ($headers as $key => $value) {
54540 $key = strtolower($key);
54541 if (!isset($normalized[$key])) {
54542 $normalized[$key] = $value;
54543 } elseif (!is_array($normalized[$key])) {
54544 $normalized[$key] = array($value);
54545 } else {
54546 $normalized[$key][] = $value;
54547 }
54548 }
54549
54550 foreach ($normalized as $key => &$value) {
54551 if (is_array($value)) {
54552 sort($value);
54553 }
54554 }
54555
54556 return $normalized;
54557 }
54558
54559 public function compareHttpHeaders($result, $expected)
54560 {
54561 // Aggregate all headers case-insensitively
54562 $result = $this->normalizeHeaders($result);
54563 $expected = $this->normalizeHeaders($expected);
54564 $this->assertEquals($result, $expected);
54565 }
54566 }
54567 <?php
54568
54569 namespace Guzzle\Tests\Parser\Message;
54570
54571 use Guzzle\Parser\Message\PeclHttpMessageParser;
54572
54573 /**
54574 * @covers Guzzle\Parser\Message\PeclHttpMessageParser
54575 */
54576 class PeclHttpMessageParserTest extends MessageParserProvider
54577 {
54578 protected function setUp()
54579 {
54580 if (!function_exists('http_parse_message')) {
54581 $this->markTestSkipped('pecl_http is not available.');
54582 }
54583 }
54584
54585 /**
54586 * @dataProvider requestProvider
54587 */
54588 public function testParsesRequests($message, $parts)
54589 {
54590 $parser = new PeclHttpMessageParser();
54591 $this->compareRequestResults($parts, $parser->parseRequest($message));
54592 }
54593
54594 /**
54595 * @dataProvider responseProvider
54596 */
54597 public function testParsesResponses($message, $parts)
54598 {
54599 $parser = new PeclHttpMessageParser();
54600 $this->compareResponseResults($parts, $parser->parseResponse($message));
54601 }
54602 }
54603 <?php
54604
54605 namespace Guzzle\Tests;
54606
54607 use Guzzle\Common\HasDispatcherInterface;
54608 use Guzzle\Common\Event;
54609 use Guzzle\Http\Message\Response;
54610 use Guzzle\Http\Message\RequestInterface;
54611 use Guzzle\Tests\Http\Message\HeaderComparison;
54612 use Guzzle\Plugin\Mock\MockPlugin;
54613 use Guzzle\Service\Client;
54614 use Guzzle\Service\Builder\ServiceBuilderInterface;
54615 use Guzzle\Service\Builder\ServiceBuilder;
54616 use Guzzle\Tests\Mock\MockObserver;
54617 use Guzzle\Tests\Http\Server;
54618 use RuntimeException;
54619 use Symfony\Component\EventDispatcher\EventSubscriberInterface;
54620
54621 /**
54622 * Base testcase class for all Guzzle testcases.
54623 */
54624 abstract class GuzzleTestCase extends \PHPUnit_Framework_TestCase
54625 {
54626 protected static $mockBasePath;
54627 public static $serviceBuilder;
54628 public static $server;
54629
54630 private $requests = array();
54631 public $mockObserver;
54632
54633 /**
54634 * Get the global server object used throughout the unit tests of Guzzle
54635 *
54636 * @return Server
54637 */
54638 public static function getServer()
54639 {
54640 if (!self::$server) {
54641 self::$server = new Server();
54642 if (self::$server->isRunning()) {
54643 self::$server->flush();
54644 } else {
54645 self::$server->start();
54646 }
54647 }
54648
54649 return self::$server;
54650 }
54651
54652 /**
54653 * Set the service builder to use for tests
54654 *
54655 * @param ServiceBuilderInterface $builder Service builder
54656 */
54657 public static function setServiceBuilder(ServiceBuilderInterface $builder)
54658 {
54659 self::$serviceBuilder = $builder;
54660 }
54661
54662 /**
54663 * Get a service builder object that can be used throughout the service tests
54664 *
54665 * @return ServiceBuilder
54666 */
54667 public static function getServiceBuilder()
54668 {
54669 if (!self::$serviceBuilder) {
54670 throw new RuntimeException('No service builder has been set via setServiceBuilder()');
54671 }
54672
54673 return self::$serviceBuilder;
54674 }
54675
54676 /**
54677 * Check if an event dispatcher has a subscriber
54678 *
54679 * @param HasDispatcherInterface $dispatcher
54680 * @param EventSubscriberInterface $subscriber
54681 *
54682 * @return bool
54683 */
54684 protected function hasSubscriber(HasDispatcherInterface $dispatcher, EventSubscriberInterface $subscriber)
54685 {
54686 $class = get_class($subscriber);
54687 $all = array_keys(call_user_func(array($class, 'getSubscribedEvents')));
54688
54689 foreach ($all as $i => $event) {
54690 foreach ($dispatcher->getEventDispatcher()->getListeners($event) as $e) {
54691 if ($e[0] === $subscriber) {
54692 unset($all[$i]);
54693 break;
54694 }
54695 }
54696 }
54697
54698 return count($all) == 0;
54699 }
54700
54701 /**
54702 * Get a wildcard observer for an event dispatcher
54703 *
54704 * @param HasDispatcherInterface $hasEvent
54705 *
54706 * @return MockObserver
54707 */
54708 public function getWildcardObserver(HasDispatcherInterface $hasDispatcher)
54709 {
54710 $class = get_class($hasDispatcher);
54711 $o = new MockObserver();
54712 $events = call_user_func(array($class, 'getAllEvents'));
54713 foreach ($events as $event) {
54714 $hasDispatcher->getEventDispatcher()->addListener($event, array($o, 'update'));
54715 }
54716
54717 return $o;
54718 }
54719
54720 /**
54721 * Set the mock response base path
54722 *
54723 * @param string $path Path to mock response folder
54724 *
54725 * @return GuzzleTestCase
54726 */
54727 public static function setMockBasePath($path)
54728 {
54729 self::$mockBasePath = $path;
54730 }
54731
54732 /**
54733 * Mark a request as being mocked
54734 *
54735 * @param RequestInterface $request
54736 *
54737 * @return self
54738 */
54739 public function addMockedRequest(RequestInterface $request)
54740 {
54741 $this->requests[] = $request;
54742
54743 return $this;
54744 }
54745
54746 /**
54747 * Get all of the mocked requests
54748 *
54749 * @return array
54750 */
54751 public function getMockedRequests()
54752 {
54753 return $this->requests;
54754 }
54755
54756 /**
54757 * Get a mock response for a client by mock file name
54758 *
54759 * @param string $path Relative path to the mock response file
54760 *
54761 * @return Response
54762 */
54763 public function getMockResponse($path)
54764 {
54765 return $path instanceof Response
54766 ? $path
54767 : MockPlugin::getMockFile(self::$mockBasePath . DIRECTORY_SEPARATOR . $path);
54768 }
54769
54770 /**
54771 * Set a mock response from a mock file on the next client request.
54772 *
54773 * This method assumes that mock response files are located under the
54774 * Command/Mock/ directory of the Service being tested
54775 * (e.g. Unfuddle/Command/Mock/). A mock response is added to the next
54776 * request sent by the client.
54777 *
54778 * @param Client $client Client object to modify
54779 * @param string $paths Path to files within the Mock folder of the service
54780 *
54781 * @return MockPlugin returns the created mock plugin
54782 */
54783 public function setMockResponse(Client $client, $paths)
54784 {
54785 $this->requests = array();
54786 $that = $this;
54787 $mock = new MockPlugin(null, true);
54788 $client->getEventDispatcher()->removeSubscriber($mock);
54789 $mock->getEventDispatcher()->addListener('mock.request', function(Event $event) use ($that) {
54790 $that->addMockedRequest($event['request']);
54791 });
54792
54793 foreach ((array) $paths as $path) {
54794 $mock->addResponse($this->getMockResponse($path));
54795 }
54796
54797 $client->getEventDispatcher()->addSubscriber($mock);
54798
54799 return $mock;
54800 }
54801
54802 /**
54803 * Compare HTTP headers and use special markup to filter values
54804 * A header prefixed with '!' means it must not exist
54805 * A header prefixed with '_' means it must be ignored
54806 * A header value of '*' means anything after the * will be ignored
54807 *
54808 * @param array $filteredHeaders Array of special headers
54809 * @param array $actualHeaders Array of headers to check against
54810 *
54811 * @return array|bool Returns an array of the differences or FALSE if none
54812 */
54813 public function compareHeaders($filteredHeaders, $actualHeaders)
54814 {
54815 $comparison = new HeaderComparison();
54816
54817 return $comparison->compare($filteredHeaders, $actualHeaders);
54818 }
54819
54820 /**
54821 * Case insensitive assertContains
54822 *
54823 * @param string $needle Search string
54824 * @param string $haystack Search this
54825 * @param string $message Optional failure message
54826 */
54827 public function assertContainsIns($needle, $haystack, $message = null)
54828 {
54829 $this->assertContains(strtolower($needle), strtolower($haystack), $message);
54830 }
54831 }
54832 <?php
54833
54834 namespace Guzzle\Tests\Http;
54835
54836 use Guzzle\Http\Exception\BadResponseException;
54837 use Guzzle\Common\Exception\RuntimeException;
54838 use Guzzle\Http\Message\Request;
54839 use Guzzle\Http\Message\Response;
54840 use Guzzle\Http\Message\RequestInterface;
54841 use Guzzle\Http\Message\RequestFactory;
54842 use Guzzle\Http\Client;
54843
54844 /**
54845 * The Server class is used to control a scripted webserver using node.js that
54846 * will respond to HTTP requests with queued responses.
54847 *
54848 * Queued responses will be served to requests using a FIFO order. All requests
54849 * received by the server are stored on the node.js server and can be retrieved
54850 * by calling {@see Server::getReceivedRequests()}.
54851 *
54852 * Mock responses that don't require data to be transmitted over HTTP a great
54853 * for testing. Mock response, however, cannot test the actual sending of an
54854 * HTTP request using cURL. This test server allows the simulation of any
54855 * number of HTTP request response transactions to test the actual sending of
54856 * requests over the wire without having to leave an internal network.
54857 */
54858 class Server
54859 {
54860 const DEFAULT_PORT = 8124;
54861 const REQUEST_DELIMITER = "\n----[request]\n";
54862
54863 /** @var int Port that the server will listen on */
54864 private $port;
54865
54866 /** @var bool Is the server running */
54867 private $running = false;
54868
54869 /** @var Client */
54870 private $client;
54871
54872 /**
54873 * Create a new scripted server
54874 *
54875 * @param int $port Port to listen on (defaults to 8124)
54876 */
54877 public function __construct($port = null)
54878 {
54879 $this->port = $port ?: self::DEFAULT_PORT;
54880 $this->client = new Client($this->getUrl());
54881 }
54882
54883 /**
54884 * Flush the received requests from the server
54885 * @throws RuntimeException
54886 */
54887 public function flush()
54888 {
54889 $this->client->delete('guzzle-server/requests')->send();
54890 }
54891
54892 /**
54893 * Queue an array of responses or a single response on the server.
54894 *
54895 * Any currently queued responses will be overwritten. Subsequent requests
54896 * on the server will return queued responses in FIFO order.
54897 *
54898 * @param array|Response $responses A single or array of Responses to queue
54899 * @throws BadResponseException
54900 */
54901 public function enqueue($responses)
54902 {
54903 $data = array();
54904 foreach ((array) $responses as $response) {
54905
54906 // Create the response object from a string
54907 if (is_string($response)) {
54908 $response = Response::fromMessage($response);
54909 } elseif (!($response instanceof Response)) {
54910 throw new BadResponseException('Responses must be strings or implement Response');
54911 }
54912
54913 $data[] = array(
54914 'statusCode' => $response->getStatusCode(),
54915 'reasonPhrase' => $response->getReasonPhrase(),
54916 'headers' => $response->getHeaders()->toArray(),
54917 'body' => $response->getBody(true)
54918 );
54919 }
54920
54921 $request = $this->client->put('guzzle-server/responses', null, json_encode($data));
54922 $request->send();
54923 }
54924
54925 /**
54926 * Check if the server is running
54927 *
54928 * @return bool
54929 */
54930 public function isRunning()
54931 {
54932 if ($this->running) {
54933 return true;
54934 }
54935
54936 try {
54937 $this->client->get('guzzle-server/perf', array(), array('timeout' => 5))->send();
54938 return $this->running = true;
54939 } catch (\Exception $e) {
54940 return false;
54941 }
54942 }
54943
54944 /**
54945 * Get the URL to the server
54946 *
54947 * @return string
54948 */
54949 public function getUrl()
54950 {
54951 return 'http://127.0.0.1:' . $this->getPort() . '/';
54952 }
54953
54954 /**
54955 * Get the port that the server is listening on
54956 *
54957 * @return int
54958 */
54959 public function getPort()
54960 {
54961 return $this->port;
54962 }
54963
54964 /**
54965 * Get all of the received requests
54966 *
54967 * @param bool $hydrate Set to TRUE to turn the messages into
54968 * actual {@see RequestInterface} objects. If $hydrate is FALSE,
54969 * requests will be returned as strings.
54970 *
54971 * @return array
54972 * @throws RuntimeException
54973 */
54974 public function getReceivedRequests($hydrate = false)
54975 {
54976 $response = $this->client->get('guzzle-server/requests')->send();
54977 $data = array_filter(explode(self::REQUEST_DELIMITER, $response->getBody(true)));
54978 if ($hydrate) {
54979 $data = array_map(function($message) {
54980 return RequestFactory::getInstance()->fromMessage($message);
54981 }, $data);
54982 }
54983
54984 return $data;
54985 }
54986
54987 /**
54988 * Start running the node.js server in the background
54989 */
54990 public function start()
54991 {
54992 if (!$this->isRunning()) {
54993 exec('node ' . __DIR__ . \DIRECTORY_SEPARATOR . 'server.js ' . $this->port . ' >> /tmp/server.log 2>&1 &');
54994 // Wait at most 5 seconds for the server the setup before proceeding
54995 $start = time();
54996 while (!$this->isRunning() && time() - $start < 5);
54997 if (!$this->running) {
54998 throw new RuntimeException(
54999 'Unable to contact server.js. Have you installed node.js v0.5.0+? node must be in your path.'
55000 );
55001 }
55002 }
55003 }
55004
55005 /**
55006 * Stop running the node.js server
55007 */
55008 public function stop()
55009 {
55010 if (!$this->isRunning()) {
55011 return false;
55012 }
55013
55014 $this->running = false;
55015 $this->client->delete('guzzle-server')->send();
55016 }
55017 }
55018 <?php
55019
55020 namespace Guzzle\Tests\Http\Curl;
55021
55022 use Guzzle\Common\Collection;
55023 use Guzzle\Common\Event;
55024 use Guzzle\Http\EntityBody;
55025 use Guzzle\Http\QueryString;
55026 use Guzzle\Http\Client;
55027 use Guzzle\Http\Message\RequestFactory;
55028 use Guzzle\Http\Message\RequestInterface;
55029 use Guzzle\Http\Curl\CurlHandle;
55030
55031 /**
55032 * @group server
55033 * @covers Guzzle\Http\Curl\CurlHandle
55034 */
55035 class CurlHandleTest extends \Guzzle\Tests\GuzzleTestCase
55036 {
55037 public $requestHandle;
55038
55039 protected function updateForHandle(RequestInterface $request)
55040 {
55041 $that = $this;
55042 $request->getEventDispatcher()->addListener('request.sent', function (Event $e) use ($that) {
55043 $that->requestHandle = $e['handle'];
55044 });
55045
55046 return $request;
55047 }
55048
55049 public function setUp()
55050 {
55051 $this->requestHandle = null;
55052 }
55053
55054 /**
55055 * @expectedException \InvalidArgumentException
55056 */
55057 public function testConstructorExpectsCurlResource()
55058 {
55059 $h = new CurlHandle(false, array());
55060 }
55061
55062 public function testConstructorExpectsProperOptions()
55063 {
55064 $h = curl_init($this->getServer()->getUrl());
55065 try {
55066 $ha = new CurlHandle($h, false);
55067 $this->fail('Expected InvalidArgumentException');
55068 } catch (\InvalidArgumentException $e) {
55069 }
55070
55071 $ha = new CurlHandle($h, array(
55072 CURLOPT_URL => $this->getServer()->getUrl()
55073 ));
55074 $this->assertEquals($this->getServer()->getUrl(), $ha->getOptions()->get(CURLOPT_URL));
55075
55076 $ha = new CurlHandle($h, new Collection(array(
55077 CURLOPT_URL => $this->getServer()->getUrl()
55078 )));
55079 $this->assertEquals($this->getServer()->getUrl(), $ha->getOptions()->get(CURLOPT_URL));
55080 }
55081
55082 public function testConstructorInitializesObject()
55083 {
55084 $handle = curl_init($this->getServer()->getUrl());
55085 $h = new CurlHandle($handle, array(
55086 CURLOPT_URL => $this->getServer()->getUrl()
55087 ));
55088 $this->assertSame($handle, $h->getHandle());
55089 $this->assertInstanceOf('Guzzle\\Http\\Url', $h->getUrl());
55090 $this->assertEquals($this->getServer()->getUrl(), (string) $h->getUrl());
55091 $this->assertEquals($this->getServer()->getUrl(), $h->getOptions()->get(CURLOPT_URL));
55092 }
55093
55094 public function testStoresStdErr()
55095 {
55096 $request = RequestFactory::getInstance()->create('GET', 'http://test.com');
55097 $request->getCurlOptions()->set('debug', true);
55098 $h = CurlHandle::factory($request);
55099 $this->assertEquals($h->getStderr(true), $h->getOptions()->get(CURLOPT_STDERR));
55100 $this->assertInternalType('resource', $h->getStderr(true));
55101 $this->assertInternalType('string', $h->getStderr(false));
55102 $r = $h->getStderr(true);
55103 fwrite($r, 'test');
55104 $this->assertEquals('test', $h->getStderr(false));
55105 }
55106
55107 public function testStoresCurlErrorNumber()
55108 {
55109 $h = new CurlHandle(curl_init('http://test.com'), array(CURLOPT_URL => 'http://test.com'));
55110 $this->assertEquals(CURLE_OK, $h->getErrorNo());
55111 $h->setErrorNo(CURLE_OPERATION_TIMEOUTED);
55112 $this->assertEquals(CURLE_OPERATION_TIMEOUTED, $h->getErrorNo());
55113 }
55114
55115 public function testAccountsForMissingStdErr()
55116 {
55117 $handle = curl_init('http://www.test.com/');
55118 $h = new CurlHandle($handle, array(
55119 CURLOPT_URL => 'http://www.test.com/'
55120 ));
55121 $this->assertNull($h->getStderr(false));
55122 }
55123
55124 public function testDeterminesIfResourceIsAvailable()
55125 {
55126 $handle = curl_init($this->getServer()->getUrl());
55127 $h = new CurlHandle($handle, array());
55128 $this->assertTrue($h->isAvailable());
55129
55130 // Mess it up by closing the handle
55131 curl_close($handle);
55132 $this->assertFalse($h->isAvailable());
55133
55134 // Mess it up by unsetting the handle
55135 $handle = null;
55136 $this->assertFalse($h->isAvailable());
55137 }
55138
55139 public function testWrapsErrorsAndInfo()
55140 {
55141 if (!defined('CURLOPT_TIMEOUT_MS')) {
55142 $this->markTestSkipped('Update curl');
55143 }
55144
55145 $settings = array(
55146 CURLOPT_PORT => 123,
55147 CURLOPT_CONNECTTIMEOUT_MS => 1,
55148 CURLOPT_TIMEOUT_MS => 1
55149 );
55150
55151 $handle = curl_init($this->getServer()->getUrl());
55152 curl_setopt_array($handle, $settings);
55153 $h = new CurlHandle($handle, $settings);
55154 @curl_exec($handle);
55155
55156 $errors = array(
55157 "couldn't connect to host",
55158 'timeout was reached',
55159 'connection time-out',
55160 'connect() timed out!'
55161 );
55162 $this->assertTrue(in_array(strtolower($h->getError()), $errors), $h->getError() . ' was not the error');
55163
55164 $this->assertTrue($h->getErrorNo() > 0);
55165
55166 $this->assertEquals($this->getServer()->getUrl(), $h->getInfo(CURLINFO_EFFECTIVE_URL));
55167 $this->assertInternalType('array', $h->getInfo());
55168
55169 curl_close($handle);
55170 $this->assertEquals(null, $h->getInfo('url'));
55171 }
55172
55173 public function testGetInfoWithoutDebugMode()
55174 {
55175 $this->getServer()->enqueue("HTTP/1.1 200 OK\r\nContent-Length: 0\r\n\r\n");
55176 $client = new Client($this->getServer()->getUrl());
55177 $request = $client->get($this->getServer()->getUrl());
55178 $response = $request->send();
55179
55180 $info = $response->getInfo();
55181 $this->assertFalse(empty($info));
55182 $this->assertEquals($this->getServer()->getUrl(), $info['url']);
55183 }
55184
55185 public function testWrapsCurlOptions()
55186 {
55187 $handle = curl_init($this->getServer()->getUrl());
55188 $h = new CurlHandle($handle, array(
55189 CURLOPT_AUTOREFERER => true,
55190 CURLOPT_BUFFERSIZE => 1024
55191 ));
55192
55193 $this->assertEquals(true, $h->getOptions()->get(CURLOPT_AUTOREFERER));
55194 $this->assertEquals(1024, $h->getOptions()->get(CURLOPT_BUFFERSIZE));
55195 }
55196
55197 /**
55198 * Data provider for factory tests
55199 *
55200 * @return array
55201 */
55202 public function dataProvider()
55203 {
55204 $testFile = __DIR__ . '/../../../../../phpunit.xml.dist';
55205
55206 $postBody = new QueryString(array('file' => '@' . $testFile));
55207 $qs = new QueryString(array(
55208 'x' => 'y',
55209 'z' => 'a'
55210 ));
55211
55212 $client = new Client();
55213 $userAgent = $client->getDefaultUserAgent();
55214 $auth = base64_encode('michael:123');
55215 $testFileSize = filesize($testFile);
55216
55217 $tests = array(
55218 // Send a regular GET
55219 array('GET', 'http://www.google.com/', null, null, array(
55220 CURLOPT_RETURNTRANSFER => 0,
55221 CURLOPT_HEADER => 0,
55222 CURLOPT_CONNECTTIMEOUT => 150,
55223 CURLOPT_WRITEFUNCTION => 'callback',
55224 CURLOPT_HEADERFUNCTION => 'callback',
55225 CURLOPT_HTTPHEADER => array('Accept:', 'Host: www.google.com', 'User-Agent: ' . $userAgent),
55226 )),
55227 // Test that custom request methods can be used
55228 array('TRACE', 'http://www.google.com/', null, null, array(
55229 CURLOPT_CUSTOMREQUEST => 'TRACE'
55230 )),
55231 // Send a GET using a port
55232 array('GET', 'http://127.0.0.1:8080', null, null, array(
55233 CURLOPT_RETURNTRANSFER => 0,
55234 CURLOPT_HEADER => 0,
55235 CURLOPT_CONNECTTIMEOUT => 150,
55236 CURLOPT_WRITEFUNCTION => 'callback',
55237 CURLOPT_HEADERFUNCTION => 'callback',
55238 CURLOPT_PORT => 8080,
55239 CURLOPT_HTTPHEADER => array('Accept:', 'Host: 127.0.0.1:8080', 'User-Agent: ' . $userAgent),
55240 )),
55241 // Send a HEAD request
55242 array('HEAD', 'http://www.google.com/', null, null, array(
55243 CURLOPT_RETURNTRANSFER => 0,
55244 CURLOPT_HEADER => 0,
55245 CURLOPT_CONNECTTIMEOUT => 150,
55246 CURLOPT_HEADERFUNCTION => 'callback',
55247 CURLOPT_HTTPHEADER => array('Accept:', 'Host: www.google.com', 'User-Agent: ' . $userAgent),
55248 CURLOPT_NOBODY => 1
55249 )),
55250 // Send a GET using basic auth
55251 array('GET', 'https://michael:123@localhost/index.html?q=2', null, null, array(
55252 CURLOPT_RETURNTRANSFER => 0,
55253 CURLOPT_HEADER => 0,
55254 CURLOPT_CONNECTTIMEOUT => 150,
55255 CURLOPT_WRITEFUNCTION => 'callback',
55256 CURLOPT_HEADERFUNCTION => 'callback',
55257 CURLOPT_HTTPHEADER => array(
55258 'Accept:',
55259 'Host: localhost',
55260 'Authorization: Basic ' . $auth,
55261 'User-Agent: ' . $userAgent
55262 ),
55263 CURLOPT_PORT => 443
55264 )),
55265 // Send a GET request with custom headers
55266 array('GET', 'http://localhost:8124/', array(
55267 'x-test-data' => 'Guzzle'
55268 ), null, array(
55269 CURLOPT_PORT => 8124,
55270 CURLOPT_HTTPHEADER => array(
55271 'Accept:',
55272 'Host: localhost:8124',
55273 'x-test-data: Guzzle',
55274 'User-Agent: ' . $userAgent
55275 )
55276 ), array(
55277 'Host' => '*',
55278 'User-Agent' => '*',
55279 'x-test-data' => 'Guzzle'
55280 )),
55281 // Send a POST using a query string
55282 array('POST', 'http://localhost:8124/post.php', null, $qs, array(
55283 CURLOPT_RETURNTRANSFER => 0,
55284 CURLOPT_HEADER => 0,
55285 CURLOPT_CONNECTTIMEOUT => 150,
55286 CURLOPT_WRITEFUNCTION => 'callback',
55287 CURLOPT_HEADERFUNCTION => 'callback',
55288 CURLOPT_POSTFIELDS => 'x=y&z=a',
55289 CURLOPT_HTTPHEADER => array (
55290 'Expect:',
55291 'Accept:',
55292 'Host: localhost:8124',
55293 'Content-Type: application/x-www-form-urlencoded; charset=utf-8',
55294 'User-Agent: ' . $userAgent
55295 )
55296 ), array(
55297 'Host' => '*',
55298 'User-Agent' => '*',
55299 'Content-Length' => '7',
55300 '!Expect' => null,
55301 'Content-Type' => 'application/x-www-form-urlencoded; charset=utf-8',
55302 '!Transfer-Encoding' => null
55303 )),
55304 // Send a PUT using raw data
55305 array('PUT', 'http://localhost:8124/put.php', null, EntityBody::factory(fopen($testFile, 'r+')), array(
55306 CURLOPT_RETURNTRANSFER => 0,
55307 CURLOPT_HEADER => 0,
55308 CURLOPT_CONNECTTIMEOUT => 150,
55309 CURLOPT_WRITEFUNCTION => 'callback',
55310 CURLOPT_HEADERFUNCTION => 'callback',
55311 CURLOPT_READFUNCTION => 'callback',
55312 CURLOPT_INFILESIZE => filesize($testFile),
55313 CURLOPT_HTTPHEADER => array (
55314 'Expect:',
55315 'Accept:',
55316 'Host: localhost:8124',
55317 'User-Agent: ' . $userAgent
55318 )
55319 ), array(
55320 'Host' => '*',
55321 'User-Agent' => '*',
55322 '!Expect' => null,
55323 'Content-Length' => $testFileSize,
55324 '!Transfer-Encoding' => null
55325 )),
55326 // Send a POST request using an array of fields
55327 array('POST', 'http://localhost:8124/post.php', null, array(
55328 'x' => 'y',
55329 'a' => 'b'
55330 ), array(
55331 CURLOPT_RETURNTRANSFER => 0,
55332 CURLOPT_HEADER => 0,
55333 CURLOPT_CONNECTTIMEOUT => 150,
55334 CURLOPT_WRITEFUNCTION => 'callback',
55335 CURLOPT_HEADERFUNCTION => 'callback',
55336 CURLOPT_POST => 1,
55337 CURLOPT_POSTFIELDS => 'x=y&a=b',
55338 CURLOPT_HTTPHEADER => array (
55339 'Expect:',
55340 'Accept:',
55341 'Host: localhost:8124',
55342 'Content-Type: application/x-www-form-urlencoded; charset=utf-8',
55343 'User-Agent: ' . $userAgent
55344 )
55345 ), array(
55346 'Host' => '*',
55347 'User-Agent' => '*',
55348 'Content-Length' => '7',
55349 '!Expect' => null,
55350 'Content-Type' => 'application/x-www-form-urlencoded; charset=utf-8',
55351 '!Transfer-Encoding' => null
55352 )),
55353 // Send a POST request with raw POST data and a custom content-type
55354 array('POST', 'http://localhost:8124/post.php', array(
55355 'Content-Type' => 'application/json'
55356 ), '{"hi":"there"}', array(
55357 CURLOPT_RETURNTRANSFER => 0,
55358 CURLOPT_HEADER => 0,
55359 CURLOPT_CONNECTTIMEOUT => 150,
55360 CURLOPT_WRITEFUNCTION => 'callback',
55361 CURLOPT_HEADERFUNCTION => 'callback',
55362 CURLOPT_CUSTOMREQUEST => 'POST',
55363 CURLOPT_UPLOAD => true,
55364 CURLOPT_INFILESIZE => 14,
55365 CURLOPT_HTTPHEADER => array (
55366 'Expect:',
55367 'Accept:',
55368 'Host: localhost:8124',
55369 'Content-Type: application/json',
55370 'User-Agent: ' . $userAgent
55371 ),
55372 ), array(
55373 'Host' => '*',
55374 'User-Agent' => '*',
55375 'Content-Type' => 'application/json',
55376 '!Expect' => null,
55377 'Content-Length' => '14',
55378 '!Transfer-Encoding' => null
55379 )),
55380 // Send a POST request with raw POST data, a custom content-type, and use chunked encoding
55381 array('POST', 'http://localhost:8124/post.php', array(
55382 'Content-Type' => 'application/json',
55383 'Transfer-Encoding' => 'chunked'
55384 ), '{"hi":"there"}', array(
55385 CURLOPT_RETURNTRANSFER => 0,
55386 CURLOPT_HEADER => 0,
55387 CURLOPT_CONNECTTIMEOUT => 150,
55388 CURLOPT_WRITEFUNCTION => 'callback',
55389 CURLOPT_HEADERFUNCTION => 'callback',
55390 CURLOPT_CUSTOMREQUEST => 'POST',
55391 CURLOPT_UPLOAD => true,
55392 CURLOPT_HTTPHEADER => array (
55393 'Expect:',
55394 'Accept:',
55395 'Host: localhost:8124',
55396 'Transfer-Encoding: chunked',
55397 'Content-Type: application/json',
55398 'User-Agent: ' . $userAgent
55399 ),
55400 ), array(
55401 'Host' => '*',
55402 'User-Agent' => '*',
55403 'Content-Type' => 'application/json',
55404 '!Expect' => null,
55405 'Transfer-Encoding' => 'chunked',
55406 '!Content-Length' => ''
55407 )),
55408 // Send a POST request with no body
55409 array('POST', 'http://localhost:8124/post.php', null, '', array(
55410 CURLOPT_CUSTOMREQUEST => 'POST',
55411 CURLOPT_HTTPHEADER => array (
55412 'Expect:',
55413 'Accept:',
55414 'Host: localhost:8124',
55415 'User-Agent: ' . $userAgent
55416 )
55417 ), array(
55418 'Host' => '*',
55419 'User-Agent' => '*',
55420 'Content-Length' => '0',
55421 '!Transfer-Encoding' => null
55422 )),
55423 // Send a POST request with empty post fields
55424 array('POST', 'http://localhost:8124/post.php', null, array(), array(
55425 CURLOPT_CUSTOMREQUEST => 'POST',
55426 CURLOPT_HTTPHEADER => array (
55427 'Expect:',
55428 'Accept:',
55429 'Host: localhost:8124',
55430 'User-Agent: ' . $userAgent
55431 )
55432 ), array(
55433 'Host' => '*',
55434 'User-Agent' => '*',
55435 'Content-Length' => '0',
55436 '!Transfer-Encoding' => null
55437 )),
55438 // Send a PATCH request
55439 array('PATCH', 'http://localhost:8124/patch.php', null, 'body', array(
55440 CURLOPT_INFILESIZE => 4,
55441 CURLOPT_HTTPHEADER => array (
55442 'Expect:',
55443 'Accept:',
55444 'Host: localhost:8124',
55445 'User-Agent: ' . $userAgent
55446 )
55447 )),
55448 // Send a DELETE request with a body
55449 array('DELETE', 'http://localhost:8124/delete.php', null, 'body', array(
55450 CURLOPT_CUSTOMREQUEST => 'DELETE',
55451 CURLOPT_INFILESIZE => 4,
55452 CURLOPT_HTTPHEADER => array (
55453 'Expect:',
55454 'Accept:',
55455 'Host: localhost:8124',
55456 'User-Agent: ' . $userAgent
55457 )
55458 ), array(
55459 'Host' => '*',
55460 'User-Agent' => '*',
55461 'Content-Length' => '4',
55462 '!Expect' => null,
55463 '!Transfer-Encoding' => null
55464 ))
55465 );
55466
55467
55468 $postTest = array('POST', 'http://localhost:8124/post.php', null, $postBody, array(
55469 CURLOPT_RETURNTRANSFER => 0,
55470 CURLOPT_HEADER => 0,
55471 CURLOPT_CONNECTTIMEOUT => 150,
55472 CURLOPT_WRITEFUNCTION => 'callback',
55473 CURLOPT_HEADERFUNCTION => 'callback',
55474 CURLOPT_POST => 1,
55475 CURLOPT_POSTFIELDS => array(
55476 'file' => '@' . $testFile . ';filename=phpunit.xml.dist;type=application/octet-stream'
55477 ),
55478 CURLOPT_HTTPHEADER => array (
55479 'Accept:',
55480 'Host: localhost:8124',
55481 'Content-Type: multipart/form-data',
55482 'Expect: 100-Continue',
55483 'User-Agent: ' . $userAgent
55484 )
55485 ), array(
55486 'Host' => '*',
55487 'User-Agent' => '*',
55488 'Content-Length' => '*',
55489 'Expect' => '100-Continue',
55490 'Content-Type' => 'multipart/form-data; boundary=*',
55491 '!Transfer-Encoding' => null
55492 ));
55493
55494 if (version_compare(phpversion(), '5.5.0', '>=')) {
55495 $postTest[4][CURLOPT_POSTFIELDS] = array(
55496 'file' => new \CurlFile($testFile, 'application/octet-stream', 'phpunit.xml.dist')
55497 );
55498 }
55499
55500 $tests[] = $postTest;
55501
55502 return $tests;
55503 }
55504
55505 /**
55506 * @dataProvider dataProvider
55507 */
55508 public function testFactoryCreatesCurlBasedOnRequest($method, $url, $headers, $body, $options, $expectedHeaders = null)
55509 {
55510 $client = new Client();
55511 $request = $client->createRequest($method, $url, $headers, $body);
55512 $request->getCurlOptions()->set('debug', true);
55513
55514 $originalRequest = clone $request;
55515 $curlTest = clone $request;
55516 $handle = CurlHandle::factory($curlTest);
55517
55518 $this->assertInstanceOf('Guzzle\\Http\\Curl\\CurlHandle', $handle);
55519 $o = $handle->getOptions()->getAll();
55520
55521 // Headers are case-insensitive
55522 if (isset($o[CURLOPT_HTTPHEADER])) {
55523 $o[CURLOPT_HTTPHEADER] = array_map('strtolower', $o[CURLOPT_HTTPHEADER]);
55524 }
55525 if (isset($options[CURLOPT_HTTPHEADER])) {
55526 $options[CURLOPT_HTTPHEADER] = array_map('strtolower', $options[CURLOPT_HTTPHEADER]);
55527 }
55528
55529 $check = 0;
55530 foreach ($options as $key => $value) {
55531 $check++;
55532 $this->assertArrayHasKey($key, $o, '-> Check number ' . $check);
55533 if ($key != CURLOPT_HTTPHEADER && $key != CURLOPT_POSTFIELDS && (is_array($o[$key])) || $o[$key] instanceof \Closure) {
55534 $this->assertEquals('callback', $value, '-> Check number ' . $check);
55535 } else {
55536 $this->assertTrue($value == $o[$key], '-> Check number ' . $check . ' - ' . var_export($value, true) . ' != ' . var_export($o[$key], true));
55537 }
55538 }
55539
55540 // If we are testing the actual sent headers
55541 if ($expectedHeaders) {
55542
55543 // Send the request to the test server
55544 $client = new Client($this->getServer()->getUrl());
55545 $request->setClient($client);
55546 $this->getServer()->flush();
55547 $this->getServer()->enqueue("HTTP/1.1 200 OK\r\nContent-Length: 0\r\n\r\n");
55548 $request->send();
55549
55550 // Get the request that was sent and create a request that we expected
55551 $requests = $this->getServer()->getReceivedRequests(true);
55552 $this->assertEquals($method, $requests[0]->getMethod());
55553
55554 $test = $this->compareHeaders($expectedHeaders, $requests[0]->getHeaders());
55555 $this->assertFalse($test, $test . "\nSent: \n" . $request . "\n\n" . $requests[0]);
55556
55557 // Ensure only one Content-Length header is sent
55558 if ($request->getHeader('Content-Length')) {
55559 $this->assertEquals((string) $request->getHeader('Content-Length'), (string) $requests[0]->getHeader('Content-Length'));
55560 }
55561 }
55562 }
55563
55564 public function testFactoryUsesSpecifiedProtocol()
55565 {
55566 $request = RequestFactory::getInstance()->create('GET', 'http://localhost:8124/');
55567 $request->setProtocolVersion('1.1');
55568 $handle = CurlHandle::factory($request);
55569 $options = $handle->getOptions();
55570 $this->assertEquals(CURL_HTTP_VERSION_1_1, $options[CURLOPT_HTTP_VERSION]);
55571 }
55572
55573 public function testUploadsPutData()
55574 {
55575 $this->getServer()->flush();
55576 $this->getServer()->enqueue("HTTP/1.1 200 OK\r\nContent-Length: 2\r\n\r\nhi");
55577
55578 $client = new Client($this->getServer()->getUrl());
55579 $request = $client->put('/');
55580 $request->getCurlOptions()->set('debug', true);
55581 $request->setBody(EntityBody::factory('test'), 'text/plain', false);
55582 $request->getCurlOptions()->set('progress', true);
55583
55584 $o = $this->getWildcardObserver($request);
55585 $request->send();
55586
55587 // Make sure that the events were dispatched
55588 $this->assertTrue($o->has('curl.callback.progress'));
55589
55590 // Ensure that the request was received exactly as intended
55591 $r = $this->getServer()->getReceivedRequests(true);
55592 $this->assertFalse($r[0]->hasHeader('Transfer-Encoding'));
55593 $this->assertEquals(4, (string) $r[0]->getHeader('Content-Length'));
55594 $sent = strtolower($r[0]);
55595 $this->assertContains('put / http/1.1', $sent);
55596 $this->assertContains('host: 127.0.0.1', $sent);
55597 $this->assertContains('user-agent:', $sent);
55598 $this->assertContains('content-type: text/plain', $sent);
55599 }
55600
55601 public function testUploadsPutDataUsingChunkedEncodingWhenLengthCannotBeDetermined()
55602 {
55603 $this->getServer()->flush();
55604 $this->getServer()->enqueue(array(
55605 "HTTP/1.1 200 OK\r\nContent-Length: 0\r\n\r\n",
55606 "HTTP/1.1 200 OK\r\nContent-Length: 2\r\n\r\nhi"
55607 ));
55608 $client = new Client($this->getServer()->getUrl());
55609 $request = $client->put('/');
55610 $request->setBody(EntityBody::factory(fopen($this->getServer()->getUrl(), 'r')), 'text/plain');
55611 $request->send();
55612
55613 $r = $this->getServer()->getReceivedRequests(true);
55614 $this->assertEquals('chunked', $r[1]->getHeader('Transfer-Encoding'));
55615 $this->assertFalse($r[1]->hasHeader('Content-Length'));
55616 }
55617
55618 public function testUploadsPutDataUsingChunkedEncodingWhenForced()
55619 {
55620 $this->getServer()->flush();
55621 $this->getServer()->enqueue("HTTP/1.1 200 OK\r\nContent-Length: 2\r\n\r\nhi");
55622
55623 $client = new Client($this->getServer()->getUrl());
55624 $request = $client->put('/', array('Transfer-Encoding' => 'chunked'), 'hi!');
55625 $request->send();
55626
55627 $r = $this->getServer()->getReceivedRequests(true);
55628 $this->assertEquals('chunked', $r[0]->getHeader('Transfer-Encoding'));
55629 $this->assertFalse($r[0]->hasHeader('Content-Length'));
55630 $this->assertEquals('hi!', $r[0]->getBody(true));
55631 }
55632
55633 public function testSendsPostRequestsWithFields()
55634 {
55635 $this->getServer()->flush();
55636 $this->getServer()->enqueue("HTTP/1.1 200 OK\r\nContent-Length: 2\r\n\r\nhi");
55637
55638 $request = RequestFactory::getInstance()->create('POST', $this->getServer()->getUrl());
55639 $request->getCurlOptions()->set('debug', true);
55640 $request->setClient(new Client());
55641 $request->addPostFields(array(
55642 'a' => 'b',
55643 'c' => 'ay! ~This is a test, isn\'t it?'
55644 ));
55645 $request->send();
55646
55647 // Make sure that the request was sent correctly
55648 $r = $this->getServer()->getReceivedRequests(true);
55649 $this->assertEquals('a=b&c=ay%21%20~This%20is%20a%20test%2C%20isn%27t%20it%3F', (string) $r[0]->getBody());
55650 $this->assertFalse($r[0]->hasHeader('Transfer-Encoding'));
55651 $this->assertEquals(56, (string) $r[0]->getHeader('Content-Length'));
55652 $sent = strtolower($r[0]);
55653 $this->assertContains('post / http/1.1', $sent);
55654 $this->assertContains('content-type: application/x-www-form-urlencoded; charset=utf-8', $sent);
55655 }
55656
55657 public function testSendsPostRequestsWithFiles()
55658 {
55659 $this->getServer()->flush();
55660 $this->getServer()->enqueue("HTTP/1.1 200 OK\r\nContent-Length: 2\r\n\r\nhi");
55661
55662 $request = RequestFactory::getInstance()->create('POST', $this->getServer()->getUrl());
55663 $request->getCurlOptions()->set('debug', true);
55664 $request->setClient(new Client());
55665 $request->addPostFiles(array(
55666 'foo' => __FILE__,
55667 ));
55668 $request->addPostFields(array(
55669 'bar' => 'baz',
55670 'arr' => array('a' => 1, 'b' => 2),
55671 ));
55672 $this->updateForHandle($request);
55673 $request->send();
55674
55675 // Ensure the CURLOPT_POSTFIELDS option was set properly
55676 $options = $this->requestHandle->getOptions()->getAll();
55677 if (version_compare(phpversion(), '5.5.0', '<')) {
55678 $this->assertContains('@' . __FILE__ . ';filename=CurlHandleTest.php;type=text/x-', $options[CURLOPT_POSTFIELDS]['foo']);
55679 } else{
55680 $this->assertInstanceOf('CURLFile', $options[CURLOPT_POSTFIELDS]['foo']);
55681 }
55682 $this->assertEquals('baz', $options[CURLOPT_POSTFIELDS]['bar']);
55683 $this->assertEquals('1', $options[CURLOPT_POSTFIELDS]['arr[a]']);
55684 $this->assertEquals('2', $options[CURLOPT_POSTFIELDS]['arr[b]']);
55685 // Ensure that a Content-Length header was sent by cURL
55686 $this->assertTrue($request->hasHeader('Content-Length'));
55687 }
55688
55689 public function testCurlConfigurationOptionsAreSet()
55690 {
55691 $request = RequestFactory::getInstance()->create('PUT', $this->getServer()->getUrl());
55692 $request->setClient(new Client('http://www.example.com'));
55693 $request->getCurlOptions()->set(CURLOPT_CONNECTTIMEOUT, 99);
55694 $request->getCurlOptions()->set('curl.fake_opt', 99);
55695 $request->getCurlOptions()->set(CURLOPT_PORT, 8181);
55696 $handle = CurlHandle::factory($request);
55697 $this->assertEquals(99, $handle->getOptions()->get(CURLOPT_CONNECTTIMEOUT));
55698 $this->assertEquals(8181, $handle->getOptions()->get(CURLOPT_PORT));
55699 $this->assertNull($handle->getOptions()->get('curl.fake_opt'));
55700 $this->assertNull($handle->getOptions()->get('fake_opt'));
55701 }
55702
55703 public function testEnsuresRequestsHaveResponsesWhenUpdatingFromTransfer()
55704 {
55705 $request = RequestFactory::getInstance()->create('PUT', $this->getServer()->getUrl());
55706 $handle = CurlHandle::factory($request);
55707 $handle->updateRequestFromTransfer($request);
55708 }
55709
55710 public function testCanSendBodyAsString()
55711 {
55712 $this->getServer()->flush();
55713 $this->getServer()->enqueue("HTTP/1.1 200 OK\r\nContent-Length: 0\r\n\r\n");
55714 $client = new Client($this->getServer()->getUrl());
55715 $request = $client->put('/', null, 'foo');
55716 $request->getCurlOptions()->set('body_as_string', true);
55717 $request->send();
55718 $requests = $this->getServer()->getReceivedRequests(false);
55719 $this->assertContains('PUT /', $requests[0]);
55720 $this->assertContains("\nfoo", $requests[0]);
55721 $this->assertContains('content-length: 3', $requests[0]);
55722 $this->assertNotContains('content-type', $requests[0]);
55723 }
55724
55725 public function testCanSendPostBodyAsString()
55726 {
55727 $this->getServer()->flush();
55728 $this->getServer()->enqueue("HTTP/1.1 200 OK\r\nContent-Length: 0\r\n\r\n");
55729 $client = new Client($this->getServer()->getUrl());
55730 $request = $client->post('/', null, 'foo');
55731 $request->getCurlOptions()->set('body_as_string', true);
55732 $request->send();
55733 $requests = $this->getServer()->getReceivedRequests(false);
55734 $this->assertContains('POST /', $requests[0]);
55735 $this->assertContains("\nfoo", $requests[0]);
55736 $this->assertContains('content-length: 3', $requests[0]);
55737 $this->assertNotContains('content-type', $requests[0]);
55738 }
55739
55740 public function testAllowsWireTransferInfoToBeEnabled()
55741 {
55742 $request = RequestFactory::getInstance()->create('PUT', $this->getServer()->getUrl());
55743 $request->getCurlOptions()->set('debug', true);
55744 $handle = CurlHandle::factory($request);
55745 $this->assertNotNull($handle->getOptions()->get(CURLOPT_STDERR));
55746 $this->assertNotNull($handle->getOptions()->get(CURLOPT_VERBOSE));
55747 }
55748
55749 public function testAddsCustomCurlOptions()
55750 {
55751 $request = RequestFactory::getInstance()->create('PUT', $this->getServer()->getUrl());
55752 $request->getCurlOptions()->set(CURLOPT_TIMEOUT, 200);
55753 $handle = CurlHandle::factory($request);
55754 $this->assertEquals(200, $handle->getOptions()->get(CURLOPT_TIMEOUT));
55755 }
55756
55757 public function testSendsPostUploadsWithContentDispositionHeaders()
55758 {
55759 $this->getServer()->flush();
55760 $this->getServer()->enqueue("HTTP/1.1 200 OK\r\n\r\nContent-Length: 0\r\n\r\n");
55761
55762 $fileToUpload = dirname(dirname(__DIR__)) . DIRECTORY_SEPARATOR . 'TestData' . DIRECTORY_SEPARATOR . 'test_service.json';
55763
55764 $client = new Client($this->getServer()->getUrl());
55765 $request = $client->post();
55766 $request->addPostFile('foo', $fileToUpload, 'application/json');
55767 $request->addPostFile('foo', __FILE__);
55768
55769 $request->send();
55770 $requests = $this->getServer()->getReceivedRequests(true);
55771 $body = (string) $requests[0]->getBody();
55772
55773 $this->assertContains('Content-Disposition: form-data; name="foo[0]"; filename="', $body);
55774 $this->assertContains('Content-Type: application/json', $body);
55775 $this->assertContains('Content-Type: text/x-', $body);
55776 $this->assertContains('Content-Disposition: form-data; name="foo[1]"; filename="', $body);
55777 }
55778
55779 public function requestMethodProvider()
55780 {
55781 return array(array('POST'), array('PUT'), array('PATCH'));
55782 }
55783
55784 /**
55785 * @dataProvider requestMethodProvider
55786 */
55787 public function testSendsRequestsWithNoBodyUsingContentLengthZero($method)
55788 {
55789 $this->getServer()->flush();
55790 $this->getServer()->enqueue("HTTP/1.1 200 OK\r\nContent-Length: 0\r\n\r\n");
55791 $client = new Client($this->getServer()->getUrl());
55792 $client->createRequest($method)->send();
55793 $requests = $this->getServer()->getReceivedRequests(true);
55794 $this->assertFalse($requests[0]->hasHeader('Transfer-Encoding'));
55795 $this->assertTrue($requests[0]->hasHeader('Content-Length'));
55796 $this->assertEquals('0', (string) $requests[0]->getHeader('Content-Length'));
55797 }
55798
55799 /**
55800 * @dataProvider provideCurlConfig
55801 */
55802 public function testParseCurlConfigConvertsStringKeysToConstantKeys($options, $expected)
55803 {
55804 $actual = CurlHandle::parseCurlConfig($options);
55805 $this->assertEquals($expected, $actual);
55806 }
55807
55808 /**
55809 * Data provider for curl configurations
55810 *
55811 * @return array
55812 */
55813 public function provideCurlConfig()
55814 {
55815 return array(
55816 // Conversion of option name to constant value
55817 array(
55818 array(
55819 'CURLOPT_PORT' => 10,
55820 'CURLOPT_TIMEOUT' => 99
55821 ),
55822 array(
55823 CURLOPT_PORT => 10,
55824 CURLOPT_TIMEOUT => 99
55825 )
55826 ),
55827 // Keeps non constant options
55828 array(
55829 array('debug' => true),
55830 array('debug' => true)
55831 ),
55832 // Conversion of constant names to constant values
55833 array(
55834 array('debug' => 'CURLPROXY_HTTP'),
55835 array('debug' => CURLPROXY_HTTP)
55836 )
55837 );
55838 }
55839
55840 public function testSeeksToBeginningOfStreamWhenSending()
55841 {
55842 $this->getServer()->flush();
55843 $this->getServer()->enqueue(array(
55844 "HTTP/1.1 200 OK\r\nContent-Length: 0\r\n\r\n",
55845 "HTTP/1.1 200 OK\r\nContent-Length: 0\r\n\r\n"
55846 ));
55847
55848 $client = new Client($this->getServer()->getUrl());
55849 $request = $client->put('/', null, 'test');
55850 $request->send();
55851 $request->send();
55852
55853 $received = $this->getServer()->getReceivedRequests(true);
55854 $this->assertEquals(2, count($received));
55855 $this->assertEquals('test', (string) $received[0]->getBody());
55856 $this->assertEquals('test', (string) $received[1]->getBody());
55857 }
55858
55859 public function testAllowsCurloptEncodingToBeSet()
55860 {
55861 $this->getServer()->flush();
55862 $this->getServer()->enqueue("HTTP/1.1 200 OK\r\nContent-Length: 0\r\n\r\n");
55863
55864 $client = new Client($this->getServer()->getUrl());
55865 $request = $client->get('/', null);
55866 $request->getCurlOptions()->set(CURLOPT_ENCODING, '');
55867 $this->updateForHandle($request);
55868 $request->send();
55869 $options = $this->requestHandle->getOptions()->getAll();
55870 $this->assertSame('', $options[CURLOPT_ENCODING]);
55871 $received = $this->getServer()->getReceivedRequests(false);
55872 $this->assertContainsIns('accept: */*', $received[0]);
55873 $this->assertContainsIns('accept-encoding: ', $received[0]);
55874 }
55875
55876 public function testSendsExpectHeaderWhenSizeIsGreaterThanCutoff()
55877 {
55878 $this->getServer()->flush();
55879 $this->getServer()->enqueue("HTTP/1.1 200 OK\r\nContent-Length: 0\r\n\r\n");
55880 $client = new Client($this->getServer()->getUrl());
55881 $request = $client->put('/', null, 'test');
55882 // Start sending the expect header to 2 bytes
55883 $this->updateForHandle($request);
55884 $request->setExpectHeaderCutoff(2)->send();
55885 $options = $this->requestHandle->getOptions()->getAll();
55886 $this->assertContains('Expect: 100-Continue', $options[CURLOPT_HTTPHEADER]);
55887 $received = $this->getServer()->getReceivedRequests(false);
55888 $this->assertContainsIns('expect: 100-continue', $received[0]);
55889 }
55890
55891 public function testSetsCurloptEncodingWhenAcceptEncodingHeaderIsSet()
55892 {
55893 $this->getServer()->flush();
55894 $this->getServer()->enqueue("HTTP/1.1 200 OK\r\nContent-Length: 4\r\n\r\ndata");
55895 $client = new Client($this->getServer()->getUrl());
55896 $request = $client->get('/', array(
55897 'Accept' => 'application/json',
55898 'Accept-Encoding' => 'gzip, deflate',
55899 ));
55900 $this->updateForHandle($request);
55901 $request->send();
55902 $options = $this->requestHandle->getOptions()->getAll();
55903 $this->assertSame('gzip, deflate', $options[CURLOPT_ENCODING]);
55904 $received = $this->getServer()->getReceivedRequests(false);
55905 $this->assertContainsIns('accept: application/json', $received[0]);
55906 $this->assertContainsIns('accept-encoding: gzip, deflate', $received[0]);
55907 }
55908
55909 public function testSendsPostFieldsForNonPostRequests()
55910 {
55911 $this->getServer()->flush();
55912 $this->getServer()->enqueue("HTTP/1.1 200 OK\r\n\r\nContent-Length: 0\r\n\r\n");
55913
55914 $client = new Client();
55915 $request = $client->put($this->getServer()->getUrl(), null, array(
55916 'foo' => 'baz',
55917 'baz' => 'bar'
55918 ));
55919
55920 $request->send();
55921 $requests = $this->getServer()->getReceivedRequests(true);
55922 $this->assertEquals('PUT', $requests[0]->getMethod());
55923 $this->assertEquals(
55924 'application/x-www-form-urlencoded; charset=utf-8',
55925 (string) $requests[0]->getHeader('Content-Type')
55926 );
55927 $this->assertEquals(15, (string) $requests[0]->getHeader('Content-Length'));
55928 $this->assertEquals('foo=baz&baz=bar', (string) $requests[0]->getBody());
55929 }
55930
55931 public function testSendsPostFilesForNonPostRequests()
55932 {
55933 $this->getServer()->flush();
55934 $this->getServer()->enqueue("HTTP/1.1 200 OK\r\n\r\nContent-Length: 0\r\n\r\n");
55935
55936 $client = new Client();
55937 $request = $client->put($this->getServer()->getUrl(), null, array(
55938 'foo' => '@' . __FILE__
55939 ));
55940
55941 $request->send();
55942 $requests = $this->getServer()->getReceivedRequests(true);
55943 $this->assertEquals('PUT', $requests[0]->getMethod());
55944 $this->assertContains('multipart/form-data', (string) $requests[0]->getHeader('Content-Type'));
55945 $this->assertContains('testSendsPostFilesForNonPostRequests', (string) $requests[0]->getBody());
55946 }
55947 }
55948 <?php
55949
55950 namespace Guzzle\Tests\Http\Curl;
55951
55952 use Guzzle\Http\Client;
55953 use Guzzle\Http\Message\Request;
55954 use Guzzle\Http\Curl\CurlMultiProxy;
55955
55956 /**
55957 * @group server
55958 * @covers Guzzle\Http\Curl\CurlMultiProxy
55959 */
55960 class CurlMultiProxyTest extends \Guzzle\Tests\GuzzleTestCase
55961 {
55962 /** @var \Guzzle\Http\Curl\CurlMultiProxy */
55963 private $multi;
55964
55965 protected function setUp()
55966 {
55967 parent::setUp();
55968 $this->multi = new CurlMultiProxy();
55969 }
55970
55971 public function tearDown()
55972 {
55973 unset($this->multi);
55974 }
55975
55976 public function testConstructorSetsMaxHandles()
55977 {
55978 $m = new CurlMultiProxy(2);
55979 $this->assertEquals(2, $this->readAttribute($m, 'maxHandles'));
55980 }
55981
55982 public function testAddingRequestsAddsToQueue()
55983 {
55984 $r = new Request('GET', 'http://www.foo.com');
55985 $this->assertSame($this->multi, $this->multi->add($r));
55986 $this->assertEquals(1, count($this->multi));
55987 $this->assertEquals(array($r), $this->multi->all());
55988
55989 $this->assertTrue($this->multi->remove($r));
55990 $this->assertFalse($this->multi->remove($r));
55991 $this->assertEquals(0, count($this->multi));
55992 }
55993
55994 public function testResetClearsState()
55995 {
55996 $r = new Request('GET', 'http://www.foo.com');
55997 $this->multi->add($r);
55998 $this->multi->reset();
55999 $this->assertEquals(0, count($this->multi));
56000 }
56001
56002 public function testSendWillSendQueuedRequestsFirst()
56003 {
56004 $this->getServer()->flush();
56005 $this->getServer()->enqueue(array(
56006 "HTTP/1.1 200 OK\r\nContent-Length: 0\r\n\r\n",
56007 "HTTP/1.1 200 OK\r\nContent-Length: 0\r\n\r\n"
56008 ));
56009 $client = new Client($this->getServer()->getUrl());
56010 $events = array();
56011 $client->getCurlMulti()->getEventDispatcher()->addListener(
56012 CurlMultiProxy::ADD_REQUEST,
56013 function ($e) use (&$events) {
56014 $events[] = $e;
56015 }
56016 );
56017 $request = $client->get();
56018 $request->getEventDispatcher()->addListener('request.complete', function () use ($client) {
56019 $client->get('/foo')->send();
56020 });
56021 $request->send();
56022 $received = $this->getServer()->getReceivedRequests(true);
56023 $this->assertEquals(2, count($received));
56024 $this->assertEquals($this->getServer()->getUrl(), $received[0]->getUrl());
56025 $this->assertEquals($this->getServer()->getUrl() . 'foo', $received[1]->getUrl());
56026 $this->assertEquals(2, count($events));
56027 }
56028
56029 public function testTrimsDownMaxHandleCount()
56030 {
56031 $this->getServer()->flush();
56032 $this->getServer()->enqueue(array(
56033 "HTTP/1.1 307 OK\r\nLocation: /foo\r\nContent-Length: 0\r\n\r\n",
56034 "HTTP/1.1 307 OK\r\nLocation: /foo\r\nContent-Length: 0\r\n\r\n",
56035 "HTTP/1.1 307 OK\r\nLocation: /foo\r\nContent-Length: 0\r\n\r\n",
56036 "HTTP/1.1 307 OK\r\nLocation: /foo\r\nContent-Length: 0\r\n\r\n",
56037 "HTTP/1.1 200 OK\r\nContent-Length: 0\r\n\r\n"
56038 ));
56039 $client = new Client($this->getServer()->getUrl());
56040 $client->setCurlMulti(new CurlMultiProxy(2));
56041 $request = $client->get();
56042 $request->send();
56043 $this->assertEquals(200, $request->getResponse()->getStatusCode());
56044 $handles = $this->readAttribute($client->getCurlMulti(), 'handles');
56045 $this->assertEquals(2, count($handles));
56046 }
56047 }
56048 <?php
56049
56050 namespace Guzzle\Tests\Http\Curl;
56051
56052 use Guzzle\Http\Client;
56053 use Guzzle\Http\Message\EntityEnclosingRequest;
56054 use Guzzle\Http\Message\Response;
56055 use Guzzle\Http\Curl\RequestMediator;
56056
56057 /**
56058 * @covers Guzzle\Http\Curl\RequestMediator
56059 */
56060 class RequestMediatorTest extends \Guzzle\Tests\GuzzleTestCase
56061 {
56062 public $events = array();
56063
56064 public function event($event)
56065 {
56066 $this->events[] = $event;
56067 }
56068
56069 public function testEmitsEvents()
56070 {
56071 $request = new EntityEnclosingRequest('PUT', 'http://www.example.com');
56072 $request->setBody('foo');
56073 $request->setResponse(new Response(200));
56074
56075 // Ensure that IO events are emitted
56076 $request->getCurlOptions()->set('emit_io', true);
56077
56078 // Attach listeners for each event type
56079 $request->getEventDispatcher()->addListener('curl.callback.progress', array($this, 'event'));
56080 $request->getEventDispatcher()->addListener('curl.callback.read', array($this, 'event'));
56081 $request->getEventDispatcher()->addListener('curl.callback.write', array($this, 'event'));
56082
56083 $mediator = new RequestMediator($request, true);
56084
56085 $mediator->progress('a', 'b', 'c', 'd');
56086 $this->assertEquals(1, count($this->events));
56087 $this->assertEquals('curl.callback.progress', $this->events[0]->getName());
56088
56089 $this->assertEquals(3, $mediator->writeResponseBody('foo', 'bar'));
56090 $this->assertEquals(2, count($this->events));
56091 $this->assertEquals('curl.callback.write', $this->events[1]->getName());
56092 $this->assertEquals('bar', $this->events[1]['write']);
56093 $this->assertSame($request, $this->events[1]['request']);
56094
56095 $this->assertEquals('foo', $mediator->readRequestBody('a', 'b', 3));
56096 $this->assertEquals(3, count($this->events));
56097 $this->assertEquals('curl.callback.read', $this->events[2]->getName());
56098 $this->assertEquals('foo', $this->events[2]['read']);
56099 $this->assertSame($request, $this->events[2]['request']);
56100 }
56101
56102 public function testDoesNotUseRequestResponseBodyWhenNotCustom()
56103 {
56104 $this->getServer()->flush();
56105 $this->getServer()->enqueue(array(
56106 "HTTP/1.1 307 Foo\r\nLocation: /foo\r\nContent-Length: 2\r\n\r\nHI",
56107 "HTTP/1.1 301 Foo\r\nLocation: /foo\r\nContent-Length: 2\r\n\r\nFI",
56108 "HTTP/1.1 200 OK\r\nContent-Length: 4\r\n\r\ntest",
56109 ));
56110 $client = new Client($this->getServer()->getUrl());
56111 $response = $client->get()->send();
56112 $this->assertEquals('test', $response->getBody(true));
56113 }
56114 }
56115 <?php
56116
56117 namespace Guzzle\Tests\Http\Curl;
56118
56119 use Guzzle\Http\Curl\CurlVersion;
56120
56121 /**
56122 * @covers Guzzle\Http\Curl\CurlVersion
56123 */
56124 class CurlVersionTest extends \Guzzle\Tests\GuzzleTestCase
56125 {
56126 public function testCachesCurlInfo()
56127 {
56128 $info = curl_version();
56129 $instance = CurlVersion::getInstance();
56130
56131 // Clear out the info cache
56132 $refObject = new \ReflectionObject($instance);
56133 $refProperty = $refObject->getProperty('version');
56134 $refProperty->setAccessible(true);
56135 $refProperty->setValue($instance, array());
56136
56137 $this->assertEquals($info, $instance->getAll());
56138 $this->assertEquals($info, $instance->getAll());
56139
56140 $this->assertEquals($info['version'], $instance->get('version'));
56141 $this->assertFalse($instance->get('foo'));
56142 }
56143
56144 public function testIsSingleton()
56145 {
56146 $refObject = new \ReflectionClass('Guzzle\Http\Curl\CurlVersion');
56147 $refProperty = $refObject->getProperty('instance');
56148 $refProperty->setAccessible(true);
56149 $refProperty->setValue(null, null);
56150
56151 $this->assertInstanceOf('Guzzle\Http\Curl\CurlVersion', CurlVersion::getInstance());
56152 }
56153 }
56154 <?php
56155
56156 namespace Guzzle\Tests\Http\Curl;
56157
56158 use Guzzle\Common\Event;
56159 use Guzzle\Http\Exception\BadResponseException;
56160 use Guzzle\Http\Exception\MultiTransferException;
56161 use Guzzle\Http\Client;
56162 use Guzzle\Http\Message\Request;
56163 use Guzzle\Http\Message\Response;
56164 use Guzzle\Http\Message\RequestFactory;
56165 use Guzzle\Http\Curl\CurlMulti;
56166 use Guzzle\Http\Exception\CurlException;
56167 use Guzzle\Tests\Mock\MockMulti;
56168
56169 /**
56170 * @group server
56171 * @covers Guzzle\Http\Curl\CurlMulti
56172 */
56173 class CurlMultiTest extends \Guzzle\Tests\GuzzleTestCase
56174 {
56175 /** @var \Guzzle\Http\Curl\CurlMulti */
56176 private $multi;
56177
56178 /**
56179 * Prepares the environment before running a test.
56180 */
56181 protected function setUp()
56182 {
56183 parent::setUp();
56184 $this->multi = new MockMulti();
56185 }
56186
56187 public function tearDown()
56188 {
56189 unset($this->multi);
56190 }
56191
56192 public function testConstructorCreateMultiHandle()
56193 {
56194 $this->assertInternalType('resource', $this->multi->getHandle());
56195 $this->assertEquals('curl_multi', get_resource_type($this->multi->getHandle()));
56196 }
56197
56198 public function testDestructorClosesMultiHandle()
56199 {
56200 $handle = $this->multi->getHandle();
56201 $this->multi->__destruct();
56202 $this->assertFalse(is_resource($handle));
56203 }
56204
56205 public function testRequestsCanBeAddedAndCounted()
56206 {
56207 $multi = new CurlMulti();
56208 $request1 = new Request('GET', 'http://www.google.com/');
56209 $multi->add($request1);
56210 $this->assertEquals(array($request1), $multi->all());
56211 $request2 = new Request('POST', 'http://www.google.com/');
56212 $multi->add($request2);
56213 $this->assertEquals(array($request1, $request2), $multi->all());
56214 $this->assertEquals(2, count($multi));
56215 }
56216
56217 public function testRequestsCanBeRemoved()
56218 {
56219 $request1 = new Request('GET', 'http://www.google.com/');
56220 $this->multi->add($request1);
56221 $request2 = new Request('PUT', 'http://www.google.com/');
56222 $this->multi->add($request2);
56223 $this->assertEquals(array($request1, $request2), $this->multi->all());
56224 $this->assertTrue($this->multi->remove($request1));
56225 $this->assertFalse($this->multi->remove($request1));
56226 $this->assertEquals(array($request2), $this->multi->all());
56227 }
56228
56229 public function testsResetRemovesRequestsAndResetsState()
56230 {
56231 $this->multi->add(new Request('GET', 'http://www.google.com/'));
56232 $this->multi->reset();
56233 $this->assertEquals(array(), $this->multi->all());
56234 }
56235
56236 public function testSendsRequestsThroughCurl()
56237 {
56238 $this->getServer()->enqueue(array(
56239 "HTTP/1.1 204 No content\r\n" .
56240 "Content-Length: 0\r\n" .
56241 "Server: Jetty(6.1.3)\r\n\r\n",
56242 "HTTP/1.1 200 OK\r\n" .
56243 "Content-Type: text/html; charset=utf-8\r\n" .
56244 "Content-Length: 4\r\n" .
56245 "Server: Jetty(6.1.3)\r\n\r\n" .
56246 "data"
56247 ));
56248
56249 $request1 = new Request('GET', $this->getServer()->getUrl());
56250 $request2 = new Request('GET', $this->getServer()->getUrl());
56251 $this->multi->add($request1);
56252 $this->multi->add($request2);
56253 $this->multi->send();
56254
56255 $response1 = $request1->getResponse();
56256 $response2 = $request2->getResponse();
56257 $this->assertInstanceOf('Guzzle\\Http\\Message\\Response', $response1);
56258 $this->assertInstanceOf('Guzzle\\Http\\Message\\Response', $response2);
56259
56260 $this->assertTrue($response1->getBody(true) == 'data' || $response2->getBody(true) == 'data');
56261 $this->assertTrue($response1->getBody(true) == '' || $response2->getBody(true) == '');
56262 $this->assertTrue($response1->getStatusCode() == '204' || $response2->getStatusCode() == '204');
56263 $this->assertNotEquals((string) $response1, (string) $response2);
56264 }
56265
56266 public function testSendsThroughCurlAndAggregatesRequestExceptions()
56267 {
56268 $this->getServer()->enqueue(array(
56269 "HTTP/1.1 200 OK\r\n" .
56270 "Content-Type: text/html; charset=utf-8\r\n" .
56271 "Content-Length: 4\r\n" .
56272 "Server: Jetty(6.1.3)\r\n" .
56273 "\r\n" .
56274 "data",
56275 "HTTP/1.1 204 No content\r\n" .
56276 "Content-Length: 0\r\n" .
56277 "Server: Jetty(6.1.3)\r\n" .
56278 "\r\n",
56279 "HTTP/1.1 404 Not Found\r\n" .
56280 "Content-Length: 0\r\n" .
56281 "\r\n"
56282 ));
56283
56284 $request1 = new Request('GET', $this->getServer()->getUrl());
56285 $request2 = new Request('HEAD', $this->getServer()->getUrl());
56286 $request3 = new Request('GET', $this->getServer()->getUrl());
56287 $this->multi->add($request1);
56288 $this->multi->add($request2);
56289 $this->multi->add($request3);
56290
56291 try {
56292 $this->multi->send();
56293 $this->fail('MultiTransferException not thrown when aggregating request exceptions');
56294 } catch (MultiTransferException $e) {
56295
56296 $this->assertTrue($e->containsRequest($request1));
56297 $this->assertTrue($e->containsRequest($request2));
56298 $this->assertTrue($e->containsRequest($request3));
56299 $this->assertInstanceOf('ArrayIterator', $e->getIterator());
56300 $this->assertEquals(1, count($e));
56301 $exceptions = $e->getIterator();
56302
56303 $response1 = $request1->getResponse();
56304 $response2 = $request2->getResponse();
56305 $response3 = $request3->getResponse();
56306
56307 $this->assertNotEquals((string) $response1, (string) $response2);
56308 $this->assertNotEquals((string) $response3, (string) $response1);
56309 $this->assertInstanceOf('Guzzle\\Http\\Message\\Response', $response1);
56310 $this->assertInstanceOf('Guzzle\\Http\\Message\\Response', $response2);
56311 $this->assertInstanceOf('Guzzle\\Http\\Message\\Response', $response3);
56312
56313 $failed = $exceptions[0]->getResponse();
56314 $this->assertEquals(404, $failed->getStatusCode());
56315 $this->assertEquals(1, count($e));
56316
56317 // Test the IteratorAggregate functionality
56318 foreach ($e as $except) {
56319 $this->assertEquals($failed, $except->getResponse());
56320 }
56321
56322 $this->assertEquals(1, count($e->getFailedRequests()));
56323 $this->assertEquals(2, count($e->getSuccessfulRequests()));
56324 $this->assertEquals(3, count($e->getAllRequests()));
56325 }
56326 }
56327
56328 public function testCurlErrorsAreCaught()
56329 {
56330 $this->getServer()->enqueue("HTTP/1.1 200 OK\r\nContent-Length: 0\r\n\r\n");
56331 try {
56332 $request = RequestFactory::getInstance()->create('GET', 'http://127.0.0.1:9876/');
56333 $request->setClient(new Client());
56334 $request->getCurlOptions()->set(CURLOPT_FRESH_CONNECT, true);
56335 $request->getCurlOptions()->set(CURLOPT_FORBID_REUSE, true);
56336 $request->getCurlOptions()->set(CURLOPT_CONNECTTIMEOUT_MS, 5);
56337 $request->send();
56338 $this->fail('CurlException not thrown');
56339 } catch (CurlException $e) {
56340 $m = $e->getMessage();
56341 $this->assertContains('[curl] ', $m);
56342 $this->assertContains('[url] http://127.0.0.1:9876/', $m);
56343 $this->assertInternalType('array', $e->getCurlInfo());
56344 }
56345 }
56346
56347 public function testRemovesQueuedRequests()
56348 {
56349 $request = RequestFactory::getInstance()->create('GET', 'http://127.0.0.1:9876/');
56350 $r = new Response(200);
56351 $request->setClient(new Client());
56352 $request->setResponse($r, true);
56353 $this->multi->add($request);
56354 $this->multi->send();
56355 $this->assertSame($r, $request->getResponse());
56356 }
56357
56358 public function testRemovesQueuedRequestsAddedInTransit()
56359 {
56360 $this->getServer()->flush();
56361 $this->getServer()->enqueue(array("HTTP/1.1 200 OK\r\nContent-Length: 0\r\n\r\n"));
56362 $client = new Client($this->getServer()->getUrl());
56363 $r = $client->get();
56364 $r->getEventDispatcher()->addListener('request.receive.status_line', function (Event $event) use ($client) {
56365 // Create a request using a queued response
56366 $request = $client->get()->setResponse(new Response(200), true);
56367 $request->send();
56368 });
56369 $r->send();
56370 $this->assertEquals(1, count($this->getServer()->getReceivedRequests(false)));
56371 }
56372
56373 public function testCatchesExceptionsBeforeSendingSingleRequest()
56374 {
56375 $client = new Client($this->getServer()->getUrl());
56376 $multi = new CurlMulti();
56377 $client->setCurlMulti($multi);
56378 $request = $client->get();
56379 $request->getEventDispatcher()->addListener('request.before_send', function() {
56380 throw new \RuntimeException('Testing!');
56381 });
56382 try {
56383 $request->send();
56384 $this->fail('Did not throw');
56385 } catch (\RuntimeException $e) {
56386 // Ensure it was removed
56387 $this->assertEquals(0, count($multi));
56388 }
56389 }
56390
56391 /**
56392 * @expectedException \Guzzle\Common\Exception\ExceptionCollection
56393 * @expectedExceptionMessage Thrown before sending!
56394 */
56395 public function testCatchesExceptionsBeforeSendingMultipleRequests()
56396 {
56397 $client = new Client($this->getServer()->getUrl());
56398 $request = $client->get();
56399 $request->getEventDispatcher()->addListener('request.before_send', function() {
56400 throw new \RuntimeException('Thrown before sending!');
56401 });
56402 $client->send(array($request));
56403 }
56404
56405 public function testCatchesExceptionsWhenRemovingQueuedRequests()
56406 {
56407 $this->getServer()->enqueue("HTTP/1.1 200 OK\r\nContent-Length: 0\r\n\r\n");
56408 $client = new Client($this->getServer()->getUrl());
56409 $r = $client->get();
56410 $r->getEventDispatcher()->addListener('request.sent', function() use ($client) {
56411 // Create a request using a queued response
56412 $client->get()->setResponse(new Response(404), true)->send();
56413 });
56414 try {
56415 $r->send();
56416 $this->fail('Did not throw');
56417 } catch (BadResponseException $e) {
56418 $this->assertCount(0, $client->getCurlMulti());
56419 }
56420 }
56421
56422 public function testCatchesExceptionsWhenRemovingQueuedRequestsBeforeSending()
56423 {
56424 $this->getServer()->enqueue("HTTP/1.1 200 OK\r\nContent-Length: 0\r\n\r\n");
56425 $client = new Client($this->getServer()->getUrl());
56426 $r = $client->get();
56427 $r->getEventDispatcher()->addListener('request.before_send', function() use ($client) {
56428 // Create a request using a queued response
56429 $client->get()->setResponse(new Response(404), true)->send();
56430 });
56431 try {
56432 $r->send();
56433 $this->fail('Did not throw');
56434 } catch (BadResponseException $e) {
56435 $this->assertCount(0, $client->getCurlMulti());
56436 }
56437 }
56438
56439 /**
56440 * @expectedException \RuntimeException
56441 * @expectedExceptionMessage test
56442 */
56443 public function testDoesNotCatchRandomExceptionsThrownDuringPerform()
56444 {
56445 $client = new Client($this->getServer()->getUrl());
56446 $multi = $this->getMock('Guzzle\\Http\\Curl\\CurlMulti', array('perform'));
56447 $multi->expects($this->once())
56448 ->method('perform')
56449 ->will($this->throwException(new \RuntimeException('test')));
56450 $multi->add($client->get());
56451 $multi->send();
56452 }
56453
56454 public function testDoesNotSendRequestsDecliningToBeSent()
56455 {
56456 if (!defined('CURLOPT_TIMEOUT_MS')) {
56457 $this->markTestSkipped('Update curl');
56458 }
56459
56460 // Create a client that is bound to fail connecting
56461 $client = new Client('http://localhost:123', array(
56462 'curl.CURLOPT_PORT' => 123,
56463 'curl.CURLOPT_CONNECTTIMEOUT_MS' => 1,
56464 ));
56465
56466 $request = $client->get();
56467 $multi = new CurlMulti();
56468 $multi->add($request);
56469
56470 // Listen for request exceptions, and when they occur, first change the
56471 // state of the request back to transferring, and then just allow it to
56472 // exception out
56473 $request->getEventDispatcher()->addListener('request.exception', function(Event $event) use ($multi) {
56474 $retries = $event['request']->getParams()->get('retries');
56475 // Allow the first failure to retry
56476 if ($retries == 0) {
56477 $event['request']->setState('transfer');
56478 $event['request']->getParams()->set('retries', 1);
56479 // Remove the request to try again
56480 $multi->remove($event['request']);
56481 $multi->add($event['request']);
56482 }
56483 });
56484
56485 try {
56486 $multi->send();
56487 $this->fail('Did not throw an exception at all!?!');
56488 } catch (\Exception $e) {
56489 $this->assertEquals(1, $request->getParams()->get('retries'));
56490 }
56491 }
56492
56493 public function testDoesNotThrowExceptionsWhenRequestsRecoverWithRetry()
56494 {
56495 $this->getServer()->flush();
56496 $client = new Client($this->getServer()->getUrl());
56497 $request = $client->get();
56498 $request->getEventDispatcher()->addListener('request.before_send', function(Event $event) {
56499 $event['request']->setResponse(new Response(200));
56500 });
56501
56502 $multi = new CurlMulti();
56503 $multi->add($request);
56504 $multi->send();
56505 $this->assertEquals(0, count($this->getServer()->getReceivedRequests(false)));
56506 }
56507
56508 public function testDoesNotThrowExceptionsWhenRequestsRecoverWithSuccess()
56509 {
56510 // Attempt a port that 99.9% is not listening
56511 $client = new Client('http://localhost:123');
56512 $request = $client->get();
56513 // Ensure it times out quickly if needed
56514 $request->getCurlOptions()->set(CURLOPT_TIMEOUT_MS, 1)->set(CURLOPT_CONNECTTIMEOUT_MS, 1);
56515
56516 $request->getEventDispatcher()->addListener('request.exception', function(Event $event) use (&$count) {
56517 $event['request']->setResponse(new Response(200));
56518 });
56519
56520 $multi = new CurlMulti();
56521 $multi->add($request);
56522 $multi->send();
56523
56524 // Ensure that the exception was caught, and the response was set manually
56525 $this->assertEquals(200, $request->getResponse()->getStatusCode());
56526 }
56527
56528 public function testHardResetReopensMultiHandle()
56529 {
56530 $this->getServer()->enqueue(array(
56531 "HTTP/1.1 200 OK\r\nContent-Length: 0\r\n\r\n",
56532 "HTTP/1.1 200 OK\r\nContent-Length: 0\r\n\r\n"
56533 ));
56534
56535 $stream = fopen('php://temp', 'w+');
56536 $client = new Client($this->getServer()->getUrl());
56537 $client->getConfig()->set('curl.CURLOPT_VERBOSE', true)->set('curl.CURLOPT_STDERR', $stream);
56538
56539 $request = $client->get();
56540 $multi = new CurlMulti();
56541 $multi->add($request);
56542 $multi->send();
56543 $multi->reset(true);
56544 $multi->add($request);
56545 $multi->send();
56546
56547 rewind($stream);
56548 $this->assertNotContains('Re-using existing connection', stream_get_contents($stream));
56549 }
56550
56551 public function testThrowsMeaningfulExceptionsForCurlMultiErrors()
56552 {
56553 $multi = new CurlMulti();
56554
56555 // Set the state of the multi object to sending to trigger the exception
56556 $reflector = new \ReflectionMethod('Guzzle\Http\Curl\CurlMulti', 'checkCurlResult');
56557 $reflector->setAccessible(true);
56558
56559 // Successful
56560 $reflector->invoke($multi, 0);
56561
56562 // Known error
56563 try {
56564 $reflector->invoke($multi, CURLM_BAD_HANDLE);
56565 $this->fail('Expected an exception here');
56566 } catch (CurlException $e) {
56567 $this->assertContains('The passed-in handle is not a valid CURLM handle.', $e->getMessage());
56568 $this->assertContains('CURLM_BAD_HANDLE', $e->getMessage());
56569 $this->assertContains(strval(CURLM_BAD_HANDLE), $e->getMessage());
56570 }
56571
56572 // Unknown error
56573 try {
56574 $reflector->invoke($multi, 255);
56575 $this->fail('Expected an exception here');
56576 } catch (CurlException $e) {
56577 $this->assertEquals('Unexpected cURL error: 255', $e->getMessage());
56578 }
56579 }
56580
56581 public function testRequestBeforeSendIncludesContentLengthHeaderIfEmptyBody()
56582 {
56583 $this->getServer()->enqueue("HTTP/1.1 200 OK\r\nContent-Length: 0\r\n\r\n");
56584 $request = new Request('PUT', $this->getServer()->getUrl());
56585 $that = $this;
56586 $request->getEventDispatcher()->addListener('request.before_send', function ($event) use ($that) {
56587 $that->assertEquals(0, $event['request']->getHeader('Content-Length'));
56588 });
56589 $this->multi->add($request);
56590 $this->multi->send();
56591 }
56592
56593 public function testRemovesConflictingTransferEncodingHeader()
56594 {
56595 $this->getServer()->flush();
56596 $this->getServer()->enqueue(array(
56597 "HTTP/1.1 200 OK\r\nContent-Length: 4\r\n\r\ntest",
56598 "HTTP/1.1 200 OK\r\nContent-Length: 0\r\n\r\n"
56599 ));
56600 $client = new Client($this->getServer()->getUrl());
56601 $request = $client->put('/', null, fopen($this->getServer()->getUrl(), 'r'));
56602 $request->setHeader('Content-Length', 4);
56603 $request->send();
56604 $received = $this->getServer()->getReceivedRequests(true);
56605 $this->assertFalse($received[1]->hasHeader('Transfer-Encoding'));
56606 $this->assertEquals(4, (string) $received[1]->getHeader('Content-Length'));
56607 }
56608 }
56609 <?php
56610
56611 namespace Guzzle\Tests\Http;
56612
56613 use Guzzle\Http\QueryString;
56614 use Guzzle\Http\QueryAggregator\CommaAggregator as Ag;
56615
56616 class CommaAggregatorTest extends \Guzzle\Tests\GuzzleTestCase
56617 {
56618 public function testAggregates()
56619 {
56620 $query = new QueryString();
56621 $a = new Ag();
56622 $key = 'test 123';
56623 $value = array('foo 123', 'baz', 'bar');
56624 $result = $a->aggregate($key, $value, $query);
56625 $this->assertEquals(array('test%20123' => 'foo%20123,baz,bar'), $result);
56626 }
56627
56628 public function testEncodes()
56629 {
56630 $query = new QueryString();
56631 $query->useUrlEncoding(false);
56632 $a = new Ag();
56633 $key = 'test 123';
56634 $value = array('foo 123', 'baz', 'bar');
56635 $result = $a->aggregate($key, $value, $query);
56636 $this->assertEquals(array('test 123' => 'foo 123,baz,bar'), $result);
56637 }
56638 }
56639 <?php
56640
56641 namespace Guzzle\Tests\Http;
56642
56643 use Guzzle\Http\QueryString;
56644 use Guzzle\Http\QueryAggregator\PhpAggregator as Ag;
56645
56646 class PhpAggregatorTest extends \Guzzle\Tests\GuzzleTestCase
56647 {
56648 public function testEncodes()
56649 {
56650 $query = new QueryString();
56651 $query->useUrlEncoding(false);
56652 $a = new Ag();
56653 $key = 't';
56654 $value = array(
56655 'v1' => 'a',
56656 'v2' => 'b',
56657 'v3' => array(
56658 'v4' => 'c',
56659 'v5' => 'd',
56660 )
56661 );
56662 $result = $a->aggregate($key, $value, $query);
56663 $this->assertEquals(array(
56664 't[v1]' => 'a',
56665 't[v2]' => 'b',
56666 't[v3][v4]' => 'c',
56667 't[v3][v5]' => 'd',
56668 ), $result);
56669 }
56670 }
56671 <?php
56672
56673 namespace Guzzle\Tests\Http;
56674
56675 use Guzzle\Http\QueryString;
56676 use Guzzle\Http\QueryAggregator\DuplicateAggregator as Ag;
56677
56678 class DuplicateAggregatorTest extends \Guzzle\Tests\GuzzleTestCase
56679 {
56680 public function testAggregates()
56681 {
56682 $query = new QueryString();
56683 $a = new Ag();
56684 $key = 'facet 1';
56685 $value = array('size a', 'width b');
56686 $result = $a->aggregate($key, $value, $query);
56687 $this->assertEquals(array('facet%201' => array('size%20a', 'width%20b')), $result);
56688 }
56689
56690 public function testEncodes()
56691 {
56692 $query = new QueryString();
56693 $query->useUrlEncoding(false);
56694 $a = new Ag();
56695 $key = 'facet 1';
56696 $value = array('size a', 'width b');
56697 $result = $a->aggregate($key, $value, $query);
56698 $this->assertEquals(array('facet 1' => array('size a', 'width b')), $result);
56699 }
56700 }
56701 <?php
56702
56703 namespace Guzzle\Tests\Http;
56704
56705 use Guzzle\Http\EntityBody;
56706 use Guzzle\Http\IoEmittingEntityBody;
56707
56708 /**
56709 * @covers Guzzle\Http\IoEmittingEntityBody
56710 */
56711 class IoEmittingEntityBodyTest extends \Guzzle\Tests\GuzzleTestCase
56712 {
56713 protected $body;
56714 protected $decorated;
56715
56716 public function setUp()
56717 {
56718 $this->decorated = EntityBody::factory('hello');
56719 $this->body = new IoEmittingEntityBody($this->decorated);
56720 }
56721
56722 public function testEmitsReadEvents()
56723 {
56724 $e = null;
56725 $this->body->getEventDispatcher()->addListener('body.read', function ($event) use (&$e) {
56726 $e = $event;
56727 });
56728 $this->assertEquals('hel', $this->body->read(3));
56729 $this->assertEquals('hel', $e['read']);
56730 $this->assertEquals(3, $e['length']);
56731 $this->assertSame($this->body, $e['body']);
56732 }
56733
56734 public function testEmitsWriteEvents()
56735 {
56736 $e = null;
56737 $this->body->getEventDispatcher()->addListener('body.write', function ($event) use (&$e) {
56738 $e = $event;
56739 });
56740 $this->body->seek(0, SEEK_END);
56741 $this->assertEquals(5, $this->body->write('there'));
56742 $this->assertEquals('there', $e['write']);
56743 $this->assertEquals(5, $e['result']);
56744 $this->assertSame($this->body, $e['body']);
56745 $this->assertEquals('hellothere', (string) $this->body);
56746 }
56747 }
56748 <?php
56749
56750 namespace Guzzle\Tests\Plugin\Redirect;
56751
56752 use Guzzle\Http\Client;
56753 use Guzzle\Http\EntityBody;
56754 use Guzzle\Http\RedirectPlugin;
56755 use Guzzle\Http\Exception\TooManyRedirectsException;
56756 use Guzzle\Plugin\History\HistoryPlugin;
56757
56758 /**
56759 * @covers Guzzle\Http\RedirectPlugin
56760 */
56761 class RedirectPluginTest extends \Guzzle\Tests\GuzzleTestCase
56762 {
56763 public function testRedirectsRequests()
56764 {
56765 // Flush the server and queue up a redirect followed by a successful response
56766 $this->getServer()->flush();
56767 $this->getServer()->enqueue(array(
56768 "HTTP/1.1 301 Moved Permanently\r\nLocation: /redirect1\r\nContent-Length: 0\r\n\r\n",
56769 "HTTP/1.1 301 Moved Permanently\r\nLocation: /redirect2\r\nContent-Length: 0\r\n\r\n",
56770 "HTTP/1.1 200 OK\r\nContent-Length: 0\r\n\r\n",
56771 ));
56772
56773 // Create a client that uses the default redirect behavior
56774 $client = new Client($this->getServer()->getUrl());
56775 $history = new HistoryPlugin();
56776 $client->addSubscriber($history);
56777
56778 $request = $client->get('/foo');
56779 $response = $request->send();
56780 $this->assertEquals(200, $response->getStatusCode());
56781 $this->assertContains('/redirect2', $response->getEffectiveUrl());
56782
56783 // Ensure that two requests were sent
56784 $requests = $this->getServer()->getReceivedRequests(true);
56785 $this->assertEquals('/foo', $requests[0]->getResource());
56786 $this->assertEquals('GET', $requests[0]->getMethod());
56787 $this->assertEquals('/redirect1', $requests[1]->getResource());
56788 $this->assertEquals('GET', $requests[1]->getMethod());
56789 $this->assertEquals('/redirect2', $requests[2]->getResource());
56790 $this->assertEquals('GET', $requests[2]->getMethod());
56791
56792 // Ensure that the redirect count was incremented
56793 $this->assertEquals(2, $request->getParams()->get(RedirectPlugin::REDIRECT_COUNT));
56794 $this->assertCount(3, $history);
56795 $requestHistory = $history->getAll();
56796
56797 $this->assertEquals(301, $requestHistory[0]['response']->getStatusCode());
56798 $this->assertEquals('/redirect1', (string) $requestHistory[0]['response']->getHeader('Location'));
56799 $this->assertEquals(301, $requestHistory[1]['response']->getStatusCode());
56800 $this->assertEquals('/redirect2', (string) $requestHistory[1]['response']->getHeader('Location'));
56801 $this->assertEquals(200, $requestHistory[2]['response']->getStatusCode());
56802 }
56803
56804 public function testCanLimitNumberOfRedirects()
56805 {
56806 // Flush the server and queue up a redirect followed by a successful response
56807 $this->getServer()->flush();
56808 $this->getServer()->enqueue(array(
56809 "HTTP/1.1 301 Moved Permanently\r\nLocation: /redirect1\r\nContent-Length: 0\r\n\r\n",
56810 "HTTP/1.1 301 Moved Permanently\r\nLocation: /redirect2\r\nContent-Length: 0\r\n\r\n",
56811 "HTTP/1.1 301 Moved Permanently\r\nLocation: /redirect3\r\nContent-Length: 0\r\n\r\n",
56812 "HTTP/1.1 301 Moved Permanently\r\nLocation: /redirect4\r\nContent-Length: 0\r\n\r\n",
56813 "HTTP/1.1 301 Moved Permanently\r\nLocation: /redirect5\r\nContent-Length: 0\r\n\r\n",
56814 "HTTP/1.1 301 Moved Permanently\r\nLocation: /redirect6\r\nContent-Length: 0\r\n\r\n"
56815 ));
56816
56817 try {
56818 $client = new Client($this->getServer()->getUrl());
56819 $client->get('/foo')->send();
56820 $this->fail('Did not throw expected exception');
56821 } catch (TooManyRedirectsException $e) {
56822 $this->assertContains(
56823 "5 redirects were issued for this request:\nGET /foo HTTP/1.1\r\n",
56824 $e->getMessage()
56825 );
56826 }
56827 }
56828
56829 public function testDefaultBehaviorIsToRedirectWithGetForEntityEnclosingRequests()
56830 {
56831 $this->getServer()->flush();
56832 $this->getServer()->enqueue(array(
56833 "HTTP/1.1 301 Moved Permanently\r\nLocation: /redirect\r\nContent-Length: 0\r\n\r\n",
56834 "HTTP/1.1 301 Moved Permanently\r\nLocation: /redirect\r\nContent-Length: 0\r\n\r\n",
56835 "HTTP/1.1 200 OK\r\nContent-Length: 0\r\n\r\n",
56836 ));
56837
56838 $client = new Client($this->getServer()->getUrl());
56839 $client->post('/foo', array('X-Baz' => 'bar'), 'testing')->send();
56840
56841 $requests = $this->getServer()->getReceivedRequests(true);
56842 $this->assertEquals('POST', $requests[0]->getMethod());
56843 $this->assertEquals('GET', $requests[1]->getMethod());
56844 $this->assertEquals('bar', (string) $requests[1]->getHeader('X-Baz'));
56845 $this->assertEquals('GET', $requests[2]->getMethod());
56846 }
56847
56848 public function testCanRedirectWithStrictRfcCompliance()
56849 {
56850 $this->getServer()->flush();
56851 $this->getServer()->enqueue(array(
56852 "HTTP/1.1 301 Moved Permanently\r\nLocation: /redirect\r\nContent-Length: 0\r\n\r\n",
56853 "HTTP/1.1 301 Moved Permanently\r\nLocation: /redirect\r\nContent-Length: 0\r\n\r\n",
56854 "HTTP/1.1 200 OK\r\nContent-Length: 0\r\n\r\n",
56855 ));
56856
56857 $client = new Client($this->getServer()->getUrl());
56858 $request = $client->post('/foo', array('X-Baz' => 'bar'), 'testing');
56859 $request->getParams()->set(RedirectPlugin::STRICT_REDIRECTS, true);
56860 $request->send();
56861
56862 $requests = $this->getServer()->getReceivedRequests(true);
56863 $this->assertEquals('POST', $requests[0]->getMethod());
56864 $this->assertEquals('POST', $requests[1]->getMethod());
56865 $this->assertEquals('bar', (string) $requests[1]->getHeader('X-Baz'));
56866 $this->assertEquals('POST', $requests[2]->getMethod());
56867 }
56868
56869 public function testRewindsStreamWhenRedirectingIfNeeded()
56870 {
56871 $this->getServer()->flush();
56872 $this->getServer()->enqueue(array(
56873 "HTTP/1.1 301 Moved Permanently\r\nLocation: /redirect\r\nContent-Length: 0\r\n\r\n",
56874 "HTTP/1.1 200 OK\r\nContent-Length: 0\r\n\r\n",
56875 ));
56876
56877 $client = new Client($this->getServer()->getUrl());
56878 $request = $client->put();
56879 $request->configureRedirects(true);
56880 $body = EntityBody::factory('foo');
56881 $body->read(1);
56882 $request->setBody($body);
56883 $request->send();
56884 $requests = $this->getServer()->getReceivedRequests(true);
56885 $this->assertEquals('foo', (string) $requests[0]->getBody());
56886 }
56887
56888 /**
56889 * @expectedException \Guzzle\Http\Exception\CouldNotRewindStreamException
56890 */
56891 public function testThrowsExceptionWhenStreamCannotBeRewound()
56892 {
56893 $this->getServer()->flush();
56894 $this->getServer()->enqueue(array(
56895 "HTTP/1.1 200 OK\r\nContent-Length: 2\r\n\r\nhi",
56896 "HTTP/1.1 301 Moved Permanently\r\nLocation: /redirect\r\nContent-Length: 0\r\n\r\n"
56897 ));
56898
56899 $client = new Client($this->getServer()->getUrl());
56900 $request = $client->put();
56901 $request->configureRedirects(true);
56902 $body = EntityBody::factory(fopen($this->getServer()->getUrl(), 'r'));
56903 $body->read(1);
56904 $request->setBody($body)->send();
56905 }
56906
56907 public function testRedirectsCanBeDisabledPerRequest()
56908 {
56909 $this->getServer()->flush();
56910 $this->getServer()->enqueue(array("HTTP/1.1 301 Foo\r\nLocation: /foo\r\nContent-Length: 0\r\n\r\n"));
56911 $client = new Client($this->getServer()->getUrl());
56912 $request = $client->put();
56913 $request->configureRedirects(false, 0);
56914 $this->assertEquals(301, $request->send()->getStatusCode());
56915 }
56916
56917 public function testCanRedirectWithNoLeadingSlashAndQuery()
56918 {
56919 $this->getServer()->flush();
56920 $this->getServer()->enqueue(array(
56921 "HTTP/1.1 301 Moved Permanently\r\nLocation: redirect?foo=bar\r\nContent-Length: 0\r\n\r\n",
56922 "HTTP/1.1 200 OK\r\nContent-Length: 0\r\n\r\n",
56923 ));
56924 $client = new Client($this->getServer()->getUrl());
56925 $request = $client->get('?foo=bar');
56926 $request->send();
56927 $requests = $this->getServer()->getReceivedRequests(true);
56928 $this->assertEquals($this->getServer()->getUrl() . '?foo=bar', $requests[0]->getUrl());
56929 $this->assertEquals($this->getServer()->getUrl() . 'redirect?foo=bar', $requests[1]->getUrl());
56930 // Ensure that the history on the actual request is correct
56931 $this->assertEquals($this->getServer()->getUrl() . '?foo=bar', $request->getUrl());
56932 }
56933
56934 public function testResetsHistoryEachSend()
56935 {
56936 // Flush the server and queue up a redirect followed by a successful response
56937 $this->getServer()->flush();
56938 $this->getServer()->enqueue(array(
56939 "HTTP/1.1 301 Moved Permanently\r\nLocation: /redirect1\r\nContent-Length: 0\r\n\r\n",
56940 "HTTP/1.1 301 Moved Permanently\r\nLocation: /redirect2\r\nContent-Length: 0\r\n\r\n",
56941 "HTTP/1.1 200 OK\r\nContent-Length: 0\r\n\r\n",
56942 "HTTP/1.1 200 OK\r\nContent-Length: 0\r\n\r\n"
56943 ));
56944
56945 // Create a client that uses the default redirect behavior
56946 $client = new Client($this->getServer()->getUrl());
56947 $history = new HistoryPlugin();
56948 $client->addSubscriber($history);
56949
56950 $request = $client->get('/foo');
56951 $response = $request->send();
56952 $this->assertEquals(3, count($history));
56953 $this->assertTrue($request->getParams()->hasKey('redirect.count'));
56954 $this->assertContains('/redirect2', $response->getEffectiveUrl());
56955
56956 $request->send();
56957 $this->assertFalse($request->getParams()->hasKey('redirect.count'));
56958 }
56959
56960 public function testHandlesRedirectsWithSpacesProperly()
56961 {
56962 // Flush the server and queue up a redirect followed by a successful response
56963 $this->getServer()->flush();
56964 $this->getServer()->enqueue(array(
56965 "HTTP/1.1 301 Moved Permanently\r\nLocation: /redirect 1\r\nContent-Length: 0\r\n\r\n",
56966 "HTTP/1.1 200 OK\r\nContent-Length: 0\r\n\r\n"
56967 ));
56968 $client = new Client($this->getServer()->getUrl());
56969 $request = $client->get('/foo');
56970 $request->send();
56971 $reqs = $this->getServer()->getReceivedRequests(true);
56972 $this->assertEquals('/redirect%201', $reqs[1]->getResource());
56973 }
56974 }
56975 <?php
56976
56977 namespace Guzzle\Tests\Http;
56978
56979 use Guzzle\Http\EntityBody;
56980 use Guzzle\Http\ReadLimitEntityBody;
56981
56982 /**
56983 * @covers Guzzle\Http\ReadLimitEntityBody
56984 */
56985 class ReadLimitEntityBodyTest extends \Guzzle\Tests\GuzzleTestCase
56986 {
56987 /** @var ReadLimitEntityBody */
56988 protected $body;
56989
56990 /** @var EntityBody */
56991 protected $decorated;
56992
56993 public function setUp()
56994 {
56995 $this->decorated = EntityBody::factory(fopen(__FILE__, 'r'));
56996 $this->body = new ReadLimitEntityBody($this->decorated, 10, 3);
56997 }
56998
56999 public function testReturnsSubsetWhenCastToString()
57000 {
57001 $body = EntityBody::factory('foo_baz_bar');
57002 $limited = new ReadLimitEntityBody($body, 3, 4);
57003 $this->assertEquals('baz', (string) $limited);
57004 }
57005
57006 public function testReturnsSubsetOfEmptyBodyWhenCastToString()
57007 {
57008 $body = EntityBody::factory('');
57009 $limited = new ReadLimitEntityBody($body, 0, 10);
57010 $this->assertEquals('', (string) $limited);
57011 }
57012
57013 public function testSeeksWhenConstructed()
57014 {
57015 $this->assertEquals(3, $this->body->ftell());
57016 }
57017
57018 public function testAllowsBoundedSeek()
57019 {
57020 $this->body->seek(100);
57021 $this->assertEquals(13, $this->body->ftell());
57022 $this->body->seek(0);
57023 $this->assertEquals(3, $this->body->ftell());
57024 $this->assertEquals(false, $this->body->seek(1000, SEEK_END));
57025 }
57026
57027 public function testReadsOnlySubsetOfData()
57028 {
57029 $data = $this->body->read(100);
57030 $this->assertEquals(10, strlen($data));
57031 $this->assertFalse($this->body->read(1000));
57032
57033 $this->body->setOffset(10);
57034 $newData = $this->body->read(100);
57035 $this->assertEquals(10, strlen($newData));
57036 $this->assertNotSame($data, $newData);
57037 }
57038
57039 public function testClaimsConsumedWhenReadLimitIsReached()
57040 {
57041 $this->assertFalse($this->body->isConsumed());
57042 $this->body->read(1000);
57043 $this->assertTrue($this->body->isConsumed());
57044 }
57045
57046 public function testContentLengthIsBounded()
57047 {
57048 $this->assertEquals(10, $this->body->getContentLength());
57049 }
57050
57051 public function testContentMd5IsBasedOnSubsection()
57052 {
57053 $this->assertNotSame($this->body->getContentMd5(), $this->decorated->getContentMd5());
57054 }
57055 }
57056 <?php
57057
57058 namespace Guzzle\Tests\Http;
57059
57060 use Guzzle\Http\EntityBody;
57061 use Guzzle\Http\CachingEntityBody;
57062
57063 /**
57064 * @covers Guzzle\Http\CachingEntityBody
57065 */
57066 class CachingEntityBodyTest extends \Guzzle\Tests\GuzzleTestCase
57067 {
57068 /** @var CachingEntityBody */
57069 protected $body;
57070
57071 /** @var EntityBody */
57072 protected $decorated;
57073
57074 public function setUp()
57075 {
57076 $this->decorated = EntityBody::factory('testing');
57077 $this->body = new CachingEntityBody($this->decorated);
57078 }
57079
57080 public function testUsesRemoteSizeIfPossible()
57081 {
57082 $body = EntityBody::factory('test');
57083 $caching = new CachingEntityBody($body);
57084 $this->assertEquals(4, $caching->getSize());
57085 $this->assertEquals(4, $caching->getContentLength());
57086 }
57087
57088 /**
57089 * @expectedException \Guzzle\Common\Exception\RuntimeException
57090 * @expectedExceptionMessage does not support custom stream rewind
57091 */
57092 public function testDoesNotAllowRewindFunction()
57093 {
57094 $this->body->setRewindFunction(true);
57095 }
57096
57097 /**
57098 * @expectedException \Guzzle\Common\Exception\RuntimeException
57099 * @expectedExceptionMessage Cannot seek to byte 10
57100 */
57101 public function testCannotSeekPastWhatHasBeenRead()
57102 {
57103 $this->body->seek(10);
57104 }
57105
57106 /**
57107 * @expectedException \Guzzle\Common\Exception\RuntimeException
57108 * @expectedExceptionMessage supports only SEEK_SET and SEEK_CUR
57109 */
57110 public function testCannotUseSeekEnd()
57111 {
57112 $this->body->seek(2, SEEK_END);
57113 }
57114
57115 public function testChangingUnderlyingStreamUpdatesSizeAndStream()
57116 {
57117 $size = filesize(__FILE__);
57118 $s = fopen(__FILE__, 'r');
57119 $this->body->setStream($s, $size);
57120 $this->assertEquals($size, $this->body->getSize());
57121 $this->assertEquals($size, $this->decorated->getSize());
57122 $this->assertSame($s, $this->body->getStream());
57123 $this->assertSame($s, $this->decorated->getStream());
57124 }
57125
57126 public function testRewindUsesSeek()
57127 {
57128 $a = EntityBody::factory('foo');
57129 $d = $this->getMockBuilder('Guzzle\Http\CachingEntityBody')
57130 ->setMethods(array('seek'))
57131 ->setConstructorArgs(array($a))
57132 ->getMock();
57133 $d->expects($this->once())
57134 ->method('seek')
57135 ->with(0)
57136 ->will($this->returnValue(true));
57137 $d->rewind();
57138 }
57139
57140 public function testCanSeekToReadBytes()
57141 {
57142 $this->assertEquals('te', $this->body->read(2));
57143 $this->body->seek(0);
57144 $this->assertEquals('test', $this->body->read(4));
57145 $this->assertEquals(4, $this->body->ftell());
57146 $this->body->seek(2);
57147 $this->assertEquals(2, $this->body->ftell());
57148 $this->body->seek(2, SEEK_CUR);
57149 $this->assertEquals(4, $this->body->ftell());
57150 $this->assertEquals('ing', $this->body->read(3));
57151 }
57152
57153 public function testWritesToBufferStream()
57154 {
57155 $this->body->read(2);
57156 $this->body->write('hi');
57157 $this->body->rewind();
57158 $this->assertEquals('tehiing', (string) $this->body);
57159 }
57160
57161 public function testReadLinesFromBothStreams()
57162 {
57163 $this->body->seek($this->body->ftell());
57164 $this->body->write("test\n123\nhello\n1234567890\n");
57165 $this->body->rewind();
57166 $this->assertEquals("test\n", $this->body->readLine(7));
57167 $this->assertEquals("123\n", $this->body->readLine(7));
57168 $this->assertEquals("hello\n", $this->body->readLine(7));
57169 $this->assertEquals("123456", $this->body->readLine(7));
57170 $this->assertEquals("7890\n", $this->body->readLine(7));
57171 // We overwrote the decorated stream, so no more data
57172 $this->assertEquals('', $this->body->readLine(7));
57173 }
57174
57175 public function testSkipsOverwrittenBytes()
57176 {
57177 $decorated = EntityBody::factory(
57178 implode("\n", array_map(function ($n) {
57179 return str_pad($n, 4, '0', STR_PAD_LEFT);
57180 }, range(0, 25)))
57181 );
57182
57183 $body = new CachingEntityBody($decorated);
57184
57185 $this->assertEquals("0000\n", $body->readLine());
57186 $this->assertEquals("0001\n", $body->readLine());
57187 // Write over part of the body yet to be read, so skip some bytes
57188 $this->assertEquals(5, $body->write("TEST\n"));
57189 $this->assertEquals(5, $this->readAttribute($body, 'skipReadBytes'));
57190 // Read, which skips bytes, then reads
57191 $this->assertEquals("0003\n", $body->readLine());
57192 $this->assertEquals(0, $this->readAttribute($body, 'skipReadBytes'));
57193 $this->assertEquals("0004\n", $body->readLine());
57194 $this->assertEquals("0005\n", $body->readLine());
57195
57196 // Overwrite part of the cached body (so don't skip any bytes)
57197 $body->seek(5);
57198 $this->assertEquals(5, $body->write("ABCD\n"));
57199 $this->assertEquals(0, $this->readAttribute($body, 'skipReadBytes'));
57200 $this->assertEquals("TEST\n", $body->readLine());
57201 $this->assertEquals("0003\n", $body->readLine());
57202 $this->assertEquals("0004\n", $body->readLine());
57203 $this->assertEquals("0005\n", $body->readLine());
57204 $this->assertEquals("0006\n", $body->readLine());
57205 $this->assertEquals(5, $body->write("1234\n"));
57206 $this->assertEquals(5, $this->readAttribute($body, 'skipReadBytes'));
57207
57208 // Seek to 0 and ensure the overwritten bit is replaced
57209 $body->rewind();
57210 $this->assertEquals("0000\nABCD\nTEST\n0003\n0004\n0005\n0006\n1234\n0008\n0009\n", $body->read(50));
57211
57212 // Ensure that casting it to a string does not include the bit that was overwritten
57213 $this->assertContains("0000\nABCD\nTEST\n0003\n0004\n0005\n0006\n1234\n0008\n0009\n", (string) $body);
57214 }
57215
57216 public function testWrapsContentType()
57217 {
57218 $a = $this->getMockBuilder('Guzzle\Http\EntityBody')
57219 ->setMethods(array('getContentType'))
57220 ->setConstructorArgs(array(fopen(__FILE__, 'r')))
57221 ->getMock();
57222 $a->expects($this->once())
57223 ->method('getContentType')
57224 ->will($this->returnValue('foo'));
57225 $d = new CachingEntityBody($a);
57226 $this->assertEquals('foo', $d->getContentType());
57227 }
57228
57229 public function testWrapsContentEncoding()
57230 {
57231 $a = $this->getMockBuilder('Guzzle\Http\EntityBody')
57232 ->setMethods(array('getContentEncoding'))
57233 ->setConstructorArgs(array(fopen(__FILE__, 'r')))
57234 ->getMock();
57235 $a->expects($this->once())
57236 ->method('getContentEncoding')
57237 ->will($this->returnValue('foo'));
57238 $d = new CachingEntityBody($a);
57239 $this->assertEquals('foo', $d->getContentEncoding());
57240 }
57241
57242 public function testWrapsMetadata()
57243 {
57244 $a = $this->getMockBuilder('Guzzle\Http\EntityBody')
57245 ->setMethods(array('getMetadata', 'getWrapper', 'getWrapperData', 'getStreamType', 'getUri'))
57246 ->setConstructorArgs(array(fopen(__FILE__, 'r')))
57247 ->getMock();
57248
57249 $a->expects($this->once())
57250 ->method('getMetadata')
57251 ->will($this->returnValue(array()));
57252 // Called twice for getWrapper and getWrapperData
57253 $a->expects($this->exactly(1))
57254 ->method('getWrapper')
57255 ->will($this->returnValue('wrapper'));
57256 $a->expects($this->once())
57257 ->method('getWrapperData')
57258 ->will($this->returnValue(array()));
57259 $a->expects($this->once())
57260 ->method('getStreamType')
57261 ->will($this->returnValue('baz'));
57262 $a->expects($this->once())
57263 ->method('getUri')
57264 ->will($this->returnValue('path/to/foo'));
57265
57266 $d = new CachingEntityBody($a);
57267 $this->assertEquals(array(), $d->getMetaData());
57268 $this->assertEquals('wrapper', $d->getWrapper());
57269 $this->assertEquals(array(), $d->getWrapperData());
57270 $this->assertEquals('baz', $d->getStreamType());
57271 $this->assertEquals('path/to/foo', $d->getUri());
57272 }
57273
57274 public function testWrapsCustomData()
57275 {
57276 $a = $this->getMockBuilder('Guzzle\Http\EntityBody')
57277 ->setMethods(array('getCustomData', 'setCustomData'))
57278 ->setConstructorArgs(array(fopen(__FILE__, 'r')))
57279 ->getMock();
57280
57281 $a->expects($this->exactly(1))
57282 ->method('getCustomData')
57283 ->with('foo')
57284 ->will($this->returnValue('bar'));
57285
57286 $a->expects($this->exactly(1))
57287 ->method('setCustomData')
57288 ->with('foo', 'bar')
57289 ->will($this->returnSelf());
57290
57291 $d = new CachingEntityBody($a);
57292 $this->assertSame($d, $d->setCustomData('foo', 'bar'));
57293 $this->assertEquals('bar', $d->getCustomData('foo'));
57294 }
57295
57296 public function testClosesBothStreams()
57297 {
57298 $s = fopen('php://temp', 'r');
57299 $a = EntityBody::factory($s);
57300 $d = new CachingEntityBody($a);
57301 $d->close();
57302 $this->assertFalse(is_resource($s));
57303 }
57304 }
57305 <?php
57306
57307 namespace Guzzle\Tests\Plugin\Redirect;
57308
57309 use Guzzle\Http\Client;
57310 use Guzzle\Http\StaticClient;
57311 use Guzzle\Plugin\Mock\MockPlugin;
57312 use Guzzle\Http\Message\Response;
57313 use Guzzle\Stream\Stream;
57314
57315 /**
57316 * @covers Guzzle\Http\StaticClient
57317 */
57318 class StaticClientTest extends \Guzzle\Tests\GuzzleTestCase
57319 {
57320 public function testMountsClient()
57321 {
57322 $client = new Client();
57323 StaticClient::mount('FooBazBar', $client);
57324 $this->assertTrue(class_exists('FooBazBar'));
57325 $this->assertSame($client, $this->readAttribute('Guzzle\Http\StaticClient', 'client'));
57326 }
57327
57328 public function requestProvider()
57329 {
57330 return array_map(
57331 function ($m) { return array($m); },
57332 array('GET', 'POST', 'PUT', 'DELETE', 'PATCH', 'HEAD', 'OPTIONS')
57333 );
57334 }
57335
57336 /**
57337 * @dataProvider requestProvider
57338 */
57339 public function testSendsRequests($method)
57340 {
57341 $mock = new MockPlugin(array(new Response(200)));
57342 call_user_func('Guzzle\Http\StaticClient::' . $method, 'http://foo.com', array(
57343 'plugins' => array($mock)
57344 ));
57345 $requests = $mock->getReceivedRequests();
57346 $this->assertCount(1, $requests);
57347 $this->assertEquals($method, $requests[0]->getMethod());
57348 }
57349
57350 public function testCanCreateStreamsUsingDefaultFactory()
57351 {
57352 $this->getServer()->enqueue(array("HTTP/1.1 200 OK\r\nContent-Length: 4\r\n\r\ntest"));
57353 $stream = StaticClient::get($this->getServer()->getUrl(), array('stream' => true));
57354 $this->assertInstanceOf('Guzzle\Stream\StreamInterface', $stream);
57355 $this->assertEquals('test', (string) $stream);
57356 }
57357
57358 public function testCanCreateStreamsUsingCustomFactory()
57359 {
57360 $stream = $this->getMockBuilder('Guzzle\Stream\StreamRequestFactoryInterface')
57361 ->setMethods(array('fromRequest'))
57362 ->getMockForAbstractClass();
57363 $resource = new Stream(fopen('php://temp', 'r+'));
57364 $stream->expects($this->once())
57365 ->method('fromRequest')
57366 ->will($this->returnValue($resource));
57367 $this->getServer()->enqueue(array("HTTP/1.1 200 OK\r\nContent-Length: 4\r\n\r\ntest"));
57368 $result = StaticClient::get($this->getServer()->getUrl(), array('stream' => $stream));
57369 $this->assertSame($resource, $result);
57370 }
57371 }
57372 /**
57373 * Guzzle node.js test server to return queued responses to HTTP requests and
57374 * expose a RESTful API for enqueueing responses and retrieving the requests
57375 * that have been received.
57376 *
57377 * - Delete all requests that have been received:
57378 * DELETE /guzzle-server/requests
57379 * Host: 127.0.0.1:8124
57380 *
57381 * - Enqueue responses
57382 * PUT /guzzle-server/responses
57383 * Host: 127.0.0.1:8124
57384 *
57385 * [{ "statusCode": 200, "reasonPhrase": "OK", "headers": {}, "body": "" }]
57386 *
57387 * - Get the received requests
57388 * GET /guzzle-server/requests
57389 * Host: 127.0.0.1:8124
57390 *
57391 * - Shutdown the server
57392 * DELETE /guzzle-server
57393 * Host: 127.0.0.1:8124
57394 *
57395 * @package Guzzle PHP <http://www.guzzlephp.org>
57396 * @license See the LICENSE file that was distributed with this source code.
57397 */
57398
57399 var http = require("http");
57400
57401 /**
57402 * Guzzle node.js server
57403 * @class
57404 */
57405 var GuzzleServer = function(port, log) {
57406
57407 this.port = port;
57408 this.log = log;
57409 this.responses = [];
57410 this.requests = [];
57411 var that = this;
57412
57413 var controlRequest = function(request, req, res) {
57414 if (req.url == '/guzzle-server/perf') {
57415 res.writeHead(200, "OK", {"Content-Length": 16});
57416 res.end("Body of response");
57417 } else if (req.method == "DELETE") {
57418 if (req.url == "/guzzle-server/requests") {
57419 // Clear the received requests
57420 that.requests = [];
57421 res.writeHead(200, "OK", { "Content-Length": 0 });
57422 res.end();
57423 if (this.log) {
57424 console.log("Flushing requests");
57425 }
57426 } else if (req.url == "/guzzle-server") {
57427 // Shutdown the server
57428 res.writeHead(200, "OK", { "Content-Length": 0, "Connection": "close" });
57429 res.end();
57430 if (this.log) {
57431 console.log("Shutting down");
57432 }
57433 that.server.close();
57434 }
57435 } else if (req.method == "GET") {
57436 if (req.url === "/guzzle-server/requests") {
57437 // Get received requests
57438 var data = that.requests.join("\n----[request]\n");
57439 res.writeHead(200, "OK", { "Content-Length": data.length });
57440 res.end(data);
57441 if (that.log) {
57442 console.log("Sending receiving requests");
57443 }
57444 }
57445 } else if (req.method == "PUT") {
57446 if (req.url == "/guzzle-server/responses") {
57447 if (that.log) {
57448 console.log("Adding responses...");
57449 }
57450 // Received response to queue
57451 var data = request.split("\r\n\r\n")[1];
57452 if (!data) {
57453 if (that.log) {
57454 console.log("No response data was provided");
57455 }
57456 res.writeHead(400, "NO RESPONSES IN REQUEST", { "Content-Length": 0 });
57457 } else {
57458 that.responses = eval("(" + data + ")");
57459 if (that.log) {
57460 console.log(that.responses);
57461 }
57462 res.writeHead(200, "OK", { "Content-Length": 0 });
57463 }
57464 res.end();
57465 }
57466 }
57467 };
57468
57469 var receivedRequest = function(request, req, res) {
57470 if (req.url.indexOf("/guzzle-server") === 0) {
57471 controlRequest(request, req, res);
57472 } else if (req.url.indexOf("/guzzle-server") == -1 && !that.responses.length) {
57473 res.writeHead(500);
57474 res.end("No responses in queue");
57475 } else {
57476 var response = that.responses.shift();
57477 res.writeHead(response.statusCode, response.reasonPhrase, response.headers);
57478 res.end(response.body);
57479 that.requests.push(request);
57480 }
57481 };
57482
57483 this.start = function() {
57484
57485 that.server = http.createServer(function(req, res) {
57486
57487 var request = req.method + " " + req.url + " HTTP/" + req.httpVersion + "\r\n";
57488 for (var i in req.headers) {
57489 request += i + ": " + req.headers[i] + "\r\n";
57490 }
57491 request += "\r\n";
57492
57493 // Receive each chunk of the request body
57494 req.addListener("data", function(chunk) {
57495 request += chunk;
57496 });
57497
57498 // Called when the request completes
57499 req.addListener("end", function() {
57500 receivedRequest(request, req, res);
57501 });
57502 });
57503 that.server.listen(port, "127.0.0.1");
57504
57505 if (this.log) {
57506 console.log("Server running at http://127.0.0.1:8124/");
57507 }
57508 };
57509 };
57510
57511 // Get the port from the arguments
57512 port = process.argv.length >= 3 ? process.argv[2] : 8124;
57513 log = process.argv.length >= 4 ? process.argv[3] : false;
57514
57515 // Start the server
57516 server = new GuzzleServer(port, log);
57517 server.start();
57518 <?php
57519
57520 namespace Guzzle\Tests\Http;
57521
57522 use Guzzle\Http\EntityBody;
57523
57524 /**
57525 * @covers Guzzle\Http\AbstractEntityBodyDecorator
57526 */
57527 class AbstractEntityBodyDecoratorTest extends \Guzzle\Tests\GuzzleTestCase
57528 {
57529 public function testDecoratesEntityBody()
57530 {
57531 $e = EntityBody::factory();
57532 $mock = $this->getMockForAbstractClass('Guzzle\Http\AbstractEntityBodyDecorator', array($e));
57533
57534 $this->assertSame($e->getStream(), $mock->getStream());
57535 $this->assertSame($e->getContentLength(), $mock->getContentLength());
57536 $this->assertSame($e->getSize(), $mock->getSize());
57537 $this->assertSame($e->getContentMd5(), $mock->getContentMd5());
57538 $this->assertSame($e->getContentType(), $mock->getContentType());
57539 $this->assertSame($e->__toString(), $mock->__toString());
57540 $this->assertSame($e->getUri(), $mock->getUri());
57541 $this->assertSame($e->getStreamType(), $mock->getStreamType());
57542 $this->assertSame($e->getWrapper(), $mock->getWrapper());
57543 $this->assertSame($e->getWrapperData(), $mock->getWrapperData());
57544 $this->assertSame($e->isReadable(), $mock->isReadable());
57545 $this->assertSame($e->isWritable(), $mock->isWritable());
57546 $this->assertSame($e->isConsumed(), $mock->isConsumed());
57547 $this->assertSame($e->isLocal(), $mock->isLocal());
57548 $this->assertSame($e->isSeekable(), $mock->isSeekable());
57549 $this->assertSame($e->getContentEncoding(), $mock->getContentEncoding());
57550 }
57551 }
57552 <?php
57553
57554 namespace Guzzle\Tests\Http\Message;
57555
57556 use Guzzle\Common\Collection;
57557 use Guzzle\Http\Client;
57558 use Guzzle\Http\Message\Response;
57559 use Guzzle\Http\Url;
57560 use Guzzle\Http\EntityBody;
57561 use Guzzle\Http\Message\RequestFactory;
57562 use Guzzle\Http\Message\Request;
57563 use Guzzle\Http\QueryString;
57564 use Guzzle\Parser\Message\MessageParser;
57565 use Guzzle\Plugin\Log\LogPlugin;
57566 use Guzzle\Plugin\Mock\MockPlugin;
57567
57568 /**
57569 * @group server
57570 * @covers Guzzle\Http\Message\RequestFactory
57571 */
57572 class HttpRequestFactoryTest extends \Guzzle\Tests\GuzzleTestCase
57573 {
57574 public function testCachesSingletonInstance()
57575 {
57576 $factory = RequestFactory::getInstance();
57577 $this->assertSame($factory, RequestFactory::getInstance());
57578 }
57579
57580 public function testCreatesNewGetRequests()
57581 {
57582 $request = RequestFactory::getInstance()->create('GET', 'http://www.google.com/');
57583 $this->assertInstanceOf('Guzzle\\Http\\Message\\MessageInterface', $request);
57584 $this->assertInstanceOf('Guzzle\\Http\\Message\\RequestInterface', $request);
57585 $this->assertInstanceOf('Guzzle\\Http\\Message\\Request', $request);
57586 $this->assertEquals('GET', $request->getMethod());
57587 $this->assertEquals('http', $request->getScheme());
57588 $this->assertEquals('http://www.google.com/', $request->getUrl());
57589 $this->assertEquals('www.google.com', $request->getHost());
57590 $this->assertEquals('/', $request->getPath());
57591 $this->assertEquals('/', $request->getResource());
57592
57593 // Create a GET request with a custom receiving body
57594 $this->getServer()->enqueue("HTTP/1.1 200 OK\r\nContent-Length: 0\r\n\r\n");
57595 $b = EntityBody::factory();
57596 $request = RequestFactory::getInstance()->create('GET', $this->getServer()->getUrl(), null, $b);
57597 $request->setClient(new Client());
57598 $response = $request->send();
57599 $this->assertSame($b, $response->getBody());
57600 }
57601
57602 public function testCreatesPutRequests()
57603 {
57604 // Test using a string
57605 $request = RequestFactory::getInstance()->create('PUT', 'http://www.google.com/path?q=1&v=2', null, 'Data');
57606 $this->assertInstanceOf('Guzzle\\Http\\Message\\EntityEnclosingRequest', $request);
57607 $this->assertEquals('PUT', $request->getMethod());
57608 $this->assertEquals('http', $request->getScheme());
57609 $this->assertEquals('http://www.google.com/path?q=1&v=2', $request->getUrl());
57610 $this->assertEquals('www.google.com', $request->getHost());
57611 $this->assertEquals('/path', $request->getPath());
57612 $this->assertEquals('/path?q=1&v=2', $request->getResource());
57613 $this->assertInstanceOf('Guzzle\\Http\\EntityBody', $request->getBody());
57614 $this->assertEquals('Data', (string) $request->getBody());
57615 unset($request);
57616
57617 // Test using an EntityBody
57618 $request = RequestFactory::getInstance()->create('PUT', 'http://www.google.com/path?q=1&v=2', null, EntityBody::factory('Data'));
57619 $this->assertInstanceOf('Guzzle\\Http\\Message\\EntityEnclosingRequest', $request);
57620 $this->assertEquals('Data', (string) $request->getBody());
57621
57622 // Test using a resource
57623 $resource = fopen('php://temp', 'w+');
57624 fwrite($resource, 'Data');
57625 $request = RequestFactory::getInstance()->create('PUT', 'http://www.google.com/path?q=1&v=2', null, $resource);
57626 $this->assertInstanceOf('Guzzle\\Http\\Message\\EntityEnclosingRequest', $request);
57627 $this->assertEquals('Data', (string) $request->getBody());
57628
57629 // Test using an object that can be cast as a string
57630 $request = RequestFactory::getInstance()->create('PUT', 'http://www.google.com/path?q=1&v=2', null, Url::factory('http://www.example.com/'));
57631 $this->assertInstanceOf('Guzzle\\Http\\Message\\EntityEnclosingRequest', $request);
57632 $this->assertEquals('http://www.example.com/', (string) $request->getBody());
57633 }
57634
57635 public function testCreatesHeadAndDeleteRequests()
57636 {
57637 $request = RequestFactory::getInstance()->create('DELETE', 'http://www.test.com/');
57638 $this->assertEquals('DELETE', $request->getMethod());
57639 $request = RequestFactory::getInstance()->create('HEAD', 'http://www.test.com/');
57640 $this->assertEquals('HEAD', $request->getMethod());
57641 }
57642
57643 public function testCreatesOptionsRequests()
57644 {
57645 $request = RequestFactory::getInstance()->create('OPTIONS', 'http://www.example.com/');
57646 $this->assertEquals('OPTIONS', $request->getMethod());
57647 $this->assertInstanceOf('Guzzle\\Http\\Message\\Request', $request);
57648 }
57649
57650 public function testCreatesNewPutRequestWithBody()
57651 {
57652 $request = RequestFactory::getInstance()->create('PUT', 'http://www.google.com/path?q=1&v=2', null, 'Data');
57653 $this->assertEquals('Data', (string) $request->getBody());
57654 }
57655
57656 public function testCreatesNewPostRequestWithFields()
57657 {
57658 // Use an array
57659 $request = RequestFactory::getInstance()->create('POST', 'http://www.google.com/path?q=1&v=2', null, array(
57660 'a' => 'b'
57661 ));
57662 $this->assertEquals(array('a' => 'b'), $request->getPostFields()->getAll());
57663 unset($request);
57664
57665 // Use a collection
57666 $request = RequestFactory::getInstance()->create('POST', 'http://www.google.com/path?q=1&v=2', null, new Collection(array(
57667 'a' => 'b'
57668 )));
57669 $this->assertEquals(array('a' => 'b'), $request->getPostFields()->getAll());
57670
57671 // Use a QueryString
57672 $request = RequestFactory::getInstance()->create('POST', 'http://www.google.com/path?q=1&v=2', null, new QueryString(array(
57673 'a' => 'b'
57674 )));
57675 $this->assertEquals(array('a' => 'b'), $request->getPostFields()->getAll());
57676
57677 $request = RequestFactory::getInstance()->create('POST', 'http://www.test.com/', null, array(
57678 'a' => 'b',
57679 'file' => '@' . __FILE__
57680 ));
57681
57682 $this->assertEquals(array(
57683 'a' => 'b'
57684 ), $request->getPostFields()->getAll());
57685
57686 $files = $request->getPostFiles();
57687 $this->assertInstanceOf('Guzzle\Http\Message\PostFile', $files['file'][0]);
57688 }
57689
57690 public function testCreatesFromParts()
57691 {
57692 $parts = parse_url('http://michael:123@www.google.com:8080/path?q=1&v=2');
57693
57694 $request = RequestFactory::getInstance()->fromParts('PUT', $parts, null, 'Data');
57695 $this->assertInstanceOf('Guzzle\\Http\\Message\\EntityEnclosingRequest', $request);
57696 $this->assertEquals('PUT', $request->getMethod());
57697 $this->assertEquals('http', $request->getScheme());
57698 $this->assertEquals('http://www.google.com:8080/path?q=1&v=2', $request->getUrl());
57699 $this->assertEquals('www.google.com', $request->getHost());
57700 $this->assertEquals('www.google.com:8080', $request->getHeader('Host'));
57701 $this->assertEquals('/path', $request->getPath());
57702 $this->assertEquals('/path?q=1&v=2', $request->getResource());
57703 $this->assertInstanceOf('Guzzle\\Http\\EntityBody', $request->getBody());
57704 $this->assertEquals('Data', (string) $request->getBody());
57705 $this->assertEquals('michael', $request->getUsername());
57706 $this->assertEquals('123', $request->getPassword());
57707 $this->assertEquals('8080', $request->getPort());
57708 $this->assertEquals(array(
57709 'scheme' => 'http',
57710 'host' => 'www.google.com',
57711 'port' => 8080,
57712 'path' => '/path',
57713 'query' => 'q=1&v=2',
57714 ), parse_url($request->getUrl()));
57715 }
57716
57717 public function testCreatesFromMessage()
57718 {
57719 $auth = base64_encode('michael:123');
57720 $message = "PUT /path?q=1&v=2 HTTP/1.1\r\nHost: www.google.com:8080\r\nContent-Length: 4\r\nAuthorization: Basic {$auth}\r\n\r\nData";
57721 $request = RequestFactory::getInstance()->fromMessage($message);
57722 $this->assertInstanceOf('Guzzle\\Http\\Message\\EntityEnclosingRequest', $request);
57723 $this->assertEquals('PUT', $request->getMethod());
57724 $this->assertEquals('http', $request->getScheme());
57725 $this->assertEquals('http://www.google.com:8080/path?q=1&v=2', $request->getUrl());
57726 $this->assertEquals('www.google.com', $request->getHost());
57727 $this->assertEquals('www.google.com:8080', $request->getHeader('Host'));
57728 $this->assertEquals('/path', $request->getPath());
57729 $this->assertEquals('/path?q=1&v=2', $request->getResource());
57730 $this->assertInstanceOf('Guzzle\\Http\\EntityBody', $request->getBody());
57731 $this->assertEquals('Data', (string) $request->getBody());
57732 $this->assertEquals("Basic {$auth}", (string) $request->getHeader('Authorization'));
57733 $this->assertEquals('8080', $request->getPort());
57734
57735 // Test passing a blank message returns false
57736 $this->assertFalse($request = RequestFactory::getInstance()->fromMessage(''));
57737
57738 // Test passing a url with no port
57739 $message = "PUT /path?q=1&v=2 HTTP/1.1\r\nHost: www.google.com\r\nContent-Length: 4\r\nAuthorization: Basic {$auth}\r\n\r\nData";
57740 $request = RequestFactory::getInstance()->fromMessage($message);
57741 $this->assertInstanceOf('Guzzle\\Http\\Message\\EntityEnclosingRequest', $request);
57742 $this->assertEquals('PUT', $request->getMethod());
57743 $this->assertEquals('http', $request->getScheme());
57744 $this->assertEquals('http://www.google.com/path?q=1&v=2', $request->getUrl());
57745 $this->assertEquals('www.google.com', $request->getHost());
57746 $this->assertEquals('/path', $request->getPath());
57747 $this->assertEquals('/path?q=1&v=2', $request->getResource());
57748 $this->assertInstanceOf('Guzzle\\Http\\EntityBody', $request->getBody());
57749 $this->assertEquals('Data', (string) $request->getBody());
57750 $this->assertEquals("Basic {$auth}", (string) $request->getHeader('Authorization'));
57751 $this->assertEquals(80, $request->getPort());
57752 }
57753
57754 public function testCreatesNewTraceRequest()
57755 {
57756 $request = RequestFactory::getInstance()->create('TRACE', 'http://www.google.com/');
57757 $this->assertFalse($request instanceof \Guzzle\Http\Message\EntityEnclosingRequest);
57758 $this->assertEquals('TRACE', $request->getMethod());
57759 }
57760
57761 public function testCreatesProperTransferEncodingRequests()
57762 {
57763 $request = RequestFactory::getInstance()->create('PUT', 'http://www.google.com/', array(
57764 'Transfer-Encoding' => 'chunked'
57765 ), 'hello');
57766 $this->assertEquals('chunked', $request->getHeader('Transfer-Encoding'));
57767 $this->assertFalse($request->hasHeader('Content-Length'));
57768 }
57769
57770 public function testProperlyDealsWithDuplicateHeaders()
57771 {
57772 $parser = new MessageParser();
57773
57774 $message = "POST / http/1.1\r\n"
57775 . "DATE:Mon, 09 Sep 2011 23:36:00 GMT\r\n"
57776 . "host:host.foo.com\r\n"
57777 . "ZOO:abc\r\n"
57778 . "ZOO:123\r\n"
57779 . "ZOO:HI\r\n"
57780 . "zoo:456\r\n\r\n";
57781
57782 $parts = $parser->parseRequest($message);
57783 $this->assertEquals(array (
57784 'DATE' => 'Mon, 09 Sep 2011 23:36:00 GMT',
57785 'host' => 'host.foo.com',
57786 'ZOO' => array('abc', '123', 'HI'),
57787 'zoo' => '456',
57788 ), $parts['headers']);
57789
57790 $request = RequestFactory::getInstance()->fromMessage($message);
57791
57792 $this->assertEquals(array(
57793 'abc', '123', 'HI', '456'
57794 ), $request->getHeader('zoo')->toArray());
57795 }
57796
57797 public function testCreatesHttpMessagesWithBodiesAndNormalizesLineEndings()
57798 {
57799 $message = "POST / http/1.1\r\n"
57800 . "Content-Type:application/x-www-form-urlencoded; charset=utf8\r\n"
57801 . "Date:Mon, 09 Sep 2011 23:36:00 GMT\r\n"
57802 . "Host:host.foo.com\r\n\r\n"
57803 . "foo=bar";
57804
57805 $request = RequestFactory::getInstance()->fromMessage($message);
57806 $this->assertEquals('application/x-www-form-urlencoded; charset=utf8', (string) $request->getHeader('Content-Type'));
57807 $this->assertEquals('foo=bar', (string) $request->getBody());
57808
57809 $message = "POST / http/1.1\n"
57810 . "Content-Type:application/x-www-form-urlencoded; charset=utf8\n"
57811 . "Date:Mon, 09 Sep 2011 23:36:00 GMT\n"
57812 . "Host:host.foo.com\n\n"
57813 . "foo=bar";
57814 $request = RequestFactory::getInstance()->fromMessage($message);
57815 $this->assertEquals('foo=bar', (string) $request->getBody());
57816
57817 $message = "PUT / HTTP/1.1\r\nContent-Length: 0\r\n\r\n";
57818 $request = RequestFactory::getInstance()->fromMessage($message);
57819 $this->assertTrue($request->hasHeader('Content-Length'));
57820 $this->assertEquals(0, (string) $request->getHeader('Content-Length'));
57821 }
57822
57823 public function testBugPathIncorrectlyHandled()
57824 {
57825 $message = "POST /foo\r\n\r\nBODY";
57826 $request = RequestFactory::getInstance()->fromMessage($message);
57827 $this->assertSame('POST', $request->getMethod());
57828 $this->assertSame('/foo', $request->getPath());
57829 $this->assertSame('BODY', (string) $request->getBody());
57830 }
57831
57832 public function testHandlesChunkedTransferEncoding()
57833 {
57834 $request = RequestFactory::getInstance()->create('PUT', 'http://www.foo.com/', array(
57835 'Transfer-Encoding' => 'chunked'
57836 ), 'Test');
57837 $this->assertFalse($request->hasHeader('Content-Length'));
57838 $this->assertEquals('chunked', $request->getHeader('Transfer-Encoding'));
57839
57840 $request = RequestFactory::getInstance()->create('POST', 'http://www.foo.com/', array(
57841 'transfer-encoding' => 'chunked'
57842 ), array(
57843 'foo' => 'bar'
57844 ));
57845
57846 $this->assertFalse($request->hasHeader('Content-Length'));
57847 $this->assertEquals('chunked', $request->getHeader('Transfer-Encoding'));
57848 }
57849
57850 public function testClonesRequestsWithMethodWithoutClient()
57851 {
57852 $f = RequestFactory::getInstance();
57853 $request = $f->create('GET', 'http://www.test.com', array('X-Foo' => 'Bar'));
57854 $request->getParams()->replace(array('test' => '123'));
57855 $request->getCurlOptions()->set('foo', 'bar');
57856 $cloned = $f->cloneRequestWithMethod($request, 'PUT');
57857 $this->assertEquals('PUT', $cloned->getMethod());
57858 $this->assertEquals('Bar', (string) $cloned->getHeader('X-Foo'));
57859 $this->assertEquals('http://www.test.com', $cloned->getUrl());
57860 // Ensure params are cloned and cleaned up
57861 $this->assertEquals(1, count($cloned->getParams()->getAll()));
57862 $this->assertEquals('123', $cloned->getParams()->get('test'));
57863 // Ensure curl options are cloned
57864 $this->assertEquals('bar', $cloned->getCurlOptions()->get('foo'));
57865 // Ensure event dispatcher is cloned
57866 $this->assertNotSame($request->getEventDispatcher(), $cloned->getEventDispatcher());
57867 }
57868
57869 public function testClonesRequestsWithMethodWithClient()
57870 {
57871 $f = RequestFactory::getInstance();
57872 $client = new Client();
57873 $request = $client->put('http://www.test.com', array('Content-Length' => 4), 'test');
57874 $cloned = $f->cloneRequestWithMethod($request, 'GET');
57875 $this->assertEquals('GET', $cloned->getMethod());
57876 $this->assertNull($cloned->getHeader('Content-Length'));
57877 $this->assertEquals('http://www.test.com', $cloned->getUrl());
57878 $this->assertSame($request->getClient(), $cloned->getClient());
57879 }
57880
57881 public function testClonesRequestsWithMethodWithClientWithEntityEnclosingChange()
57882 {
57883 $f = RequestFactory::getInstance();
57884 $client = new Client();
57885 $request = $client->put('http://www.test.com', array('Content-Length' => 4), 'test');
57886 $cloned = $f->cloneRequestWithMethod($request, 'POST');
57887 $this->assertEquals('POST', $cloned->getMethod());
57888 $this->assertEquals('test', (string) $cloned->getBody());
57889 }
57890
57891 public function testCanDisableRedirects()
57892 {
57893 $this->getServer()->enqueue(array(
57894 "HTTP/1.1 307\r\nLocation: " . $this->getServer()->getUrl() . "\r\nContent-Length: 0\r\n\r\n"
57895 ));
57896 $client = new Client($this->getServer()->getUrl());
57897 $response = $client->get('/', array(), array('allow_redirects' => false))->send();
57898 $this->assertEquals(307, $response->getStatusCode());
57899 }
57900
57901 public function testCanAddCookies()
57902 {
57903 $client = new Client($this->getServer()->getUrl());
57904 $request = $client->get('/', array(), array('cookies' => array('Foo' => 'Bar')));
57905 $this->assertEquals('Bar', $request->getCookie('Foo'));
57906 }
57907
57908 public function testCanAddQueryString()
57909 {
57910 $request = RequestFactory::getInstance()->create('GET', 'http://foo.com', array(), null, array(
57911 'query' => array('Foo' => 'Bar')
57912 ));
57913 $this->assertEquals('Bar', $request->getQuery()->get('Foo'));
57914 }
57915
57916 public function testCanSetDefaultQueryString()
57917 {
57918 $request = new Request('GET', 'http://www.foo.com?test=abc');
57919 RequestFactory::getInstance()->applyOptions($request, array(
57920 'query' => array('test' => '123', 'other' => 't123')
57921 ), RequestFactory::OPTIONS_AS_DEFAULTS);
57922 $this->assertEquals('abc', $request->getQuery()->get('test'));
57923 $this->assertEquals('t123', $request->getQuery()->get('other'));
57924 }
57925
57926 public function testCanAddBasicAuth()
57927 {
57928 $request = RequestFactory::getInstance()->create('GET', 'http://foo.com', array(), null, array(
57929 'auth' => array('michael', 'test')
57930 ));
57931 $this->assertEquals('michael', $request->getUsername());
57932 $this->assertEquals('test', $request->getPassword());
57933 }
57934
57935 public function testCanAddDigestAuth()
57936 {
57937 $request = RequestFactory::getInstance()->create('GET', 'http://foo.com', array(), null, array(
57938 'auth' => array('michael', 'test', 'digest')
57939 ));
57940 $this->assertEquals(CURLAUTH_DIGEST, $request->getCurlOptions()->get(CURLOPT_HTTPAUTH));
57941 $this->assertEquals('michael', $request->getUsername());
57942 $this->assertEquals('test', $request->getPassword());
57943 }
57944
57945 public function testCanAddEvents()
57946 {
57947 $foo = null;
57948 $client = new Client();
57949 $client->addSubscriber(new MockPlugin(array(new Response(200))));
57950 $request = $client->get($this->getServer()->getUrl(), array(), array(
57951 'events' => array(
57952 'request.before_send' => function () use (&$foo) { $foo = true; }
57953 )
57954 ));
57955 $request->send();
57956 $this->assertTrue($foo);
57957 }
57958
57959 public function testCanAddEventsWithPriority()
57960 {
57961 $foo = null;
57962 $client = new Client();
57963 $client->addSubscriber(new MockPlugin(array(new Response(200))));
57964 $request = $client->get($this->getServer()->getUrl(), array(), array(
57965 'events' => array(
57966 'request.before_send' => array(function () use (&$foo) { $foo = true; }, 100)
57967 )
57968 ));
57969 $request->send();
57970 $this->assertTrue($foo);
57971 }
57972
57973 public function testCanAddPlugins()
57974 {
57975 $mock = new MockPlugin(array(new Response(200)));
57976 $client = new Client();
57977 $client->addSubscriber($mock);
57978 $request = $client->get('/', array(), array(
57979 'plugins' => array($mock)
57980 ));
57981 $request->send();
57982 }
57983
57984 public function testCanDisableExceptions()
57985 {
57986 $client = new Client();
57987 $request = $client->get('/', array(), array(
57988 'plugins' => array(new MockPlugin(array(new Response(500)))),
57989 'exceptions' => false
57990 ));
57991 $this->assertEquals(500, $request->send()->getStatusCode());
57992 }
57993
57994 public function testCanDisableExceptionsWithErrorListener()
57995 {
57996 $client = new Client();
57997 $client->getEventDispatcher()->addListener('request.error', function () {});
57998 $request = $client->get('/', array(), array(
57999 'plugins' => array(new MockPlugin(array(new Response(500)))),
58000 'exceptions' => false
58001 ));
58002 $this->assertEquals(500, $request->send()->getStatusCode());
58003 }
58004
58005 public function testCanChangeSaveToLocation()
58006 {
58007 $r = EntityBody::factory();
58008 $client = new Client();
58009 $request = $client->get('/', array(), array(
58010 'plugins' => array(new MockPlugin(array(new Response(200, array(), 'testing')))),
58011 'save_to' => $r
58012 ));
58013 $request->send();
58014 $this->assertEquals('testing', (string) $r);
58015 }
58016
58017 public function testCanSetProxy()
58018 {
58019 $client = new Client();
58020 $request = $client->get('/', array(), array('proxy' => '192.168.16.121'));
58021 $this->assertEquals('192.168.16.121', $request->getCurlOptions()->get(CURLOPT_PROXY));
58022 }
58023
58024 public function testCanSetHeadersOption()
58025 {
58026 $client = new Client();
58027 $request = $client->get('/', array(), array('headers' => array('Foo' => 'Bar')));
58028 $this->assertEquals('Bar', (string) $request->getHeader('Foo'));
58029 }
58030
58031 public function testCanSetDefaultHeadersOptions()
58032 {
58033 $request = new Request('GET', 'http://www.foo.com', array('Foo' => 'Bar'));
58034 RequestFactory::getInstance()->applyOptions($request, array(
58035 'headers' => array('Foo' => 'Baz', 'Bam' => 't123')
58036 ), RequestFactory::OPTIONS_AS_DEFAULTS);
58037 $this->assertEquals('Bar', (string) $request->getHeader('Foo'));
58038 $this->assertEquals('t123', (string) $request->getHeader('Bam'));
58039 }
58040
58041 public function testCanSetBodyOption()
58042 {
58043 $client = new Client();
58044 $request = $client->put('/', array(), null, array('body' => 'test'));
58045 $this->assertEquals('test', (string) $request->getBody());
58046 }
58047
58048 /**
58049 * @expectedException \InvalidArgumentException
58050 */
58051 public function testValidatesBodyOption()
58052 {
58053 $client = new Client();
58054 $client->get('/', array(), array('body' => 'test'));
58055 }
58056
58057 public function testCanSetTimeoutOption()
58058 {
58059 $client = new Client();
58060 $request = $client->get('/', array(), array('timeout' => 1.5));
58061 $this->assertEquals(1500, $request->getCurlOptions()->get(CURLOPT_TIMEOUT_MS));
58062 }
58063
58064 public function testCanSetConnectTimeoutOption()
58065 {
58066 $client = new Client();
58067 $request = $client->get('/', array(), array('connect_timeout' => 1.5));
58068 $this->assertEquals(1500, $request->getCurlOptions()->get(CURLOPT_CONNECTTIMEOUT_MS));
58069 }
58070
58071 public function testCanSetDebug()
58072 {
58073 $client = new Client();
58074 $request = $client->get('/', array(), array('debug' => true));
58075 $match = false;
58076 foreach ($request->getEventDispatcher()->getListeners('request.sent') as $l) {
58077 if ($l[0] instanceof LogPlugin) {
58078 $match = true;
58079 break;
58080 }
58081 }
58082 $this->assertTrue($match);
58083 }
58084
58085 public function testCanSetVerifyToOff()
58086 {
58087 $client = new Client();
58088 $request = $client->get('/', array(), array('verify' => false));
58089 $this->assertNull($request->getCurlOptions()->get(CURLOPT_CAINFO));
58090 $this->assertSame(0, $request->getCurlOptions()->get(CURLOPT_SSL_VERIFYHOST));
58091 $this->assertFalse($request->getCurlOptions()->get(CURLOPT_SSL_VERIFYPEER));
58092 }
58093
58094 public function testCanSetVerifyToOn()
58095 {
58096 $client = new Client();
58097 $request = $client->get('/', array(), array('verify' => true));
58098 $this->assertNotNull($request->getCurlOptions()->get(CURLOPT_CAINFO));
58099 $this->assertSame(2, $request->getCurlOptions()->get(CURLOPT_SSL_VERIFYHOST));
58100 $this->assertTrue($request->getCurlOptions()->get(CURLOPT_SSL_VERIFYPEER));
58101 }
58102
58103 public function testCanSetVerifyToPath()
58104 {
58105 $client = new Client();
58106 $request = $client->get('/', array(), array('verify' => '/foo.pem'));
58107 $this->assertEquals('/foo.pem', $request->getCurlOptions()->get(CURLOPT_CAINFO));
58108 $this->assertSame(2, $request->getCurlOptions()->get(CURLOPT_SSL_VERIFYHOST));
58109 $this->assertTrue($request->getCurlOptions()->get(CURLOPT_SSL_VERIFYPEER));
58110 }
58111
58112 public function inputValidation()
58113 {
58114 return array_map(function ($option) { return array($option); }, array(
58115 'headers', 'query', 'cookies', 'auth', 'events', 'plugins', 'params'
58116 ));
58117 }
58118
58119 /**
58120 * @dataProvider inputValidation
58121 * @expectedException \Guzzle\Common\Exception\InvalidArgumentException
58122 */
58123 public function testValidatesInput($option)
58124 {
58125 $client = new Client();
58126 $client->get('/', array(), array($option => 'foo'));
58127 }
58128
58129 public function testCanAddRequestParams()
58130 {
58131 $client = new Client();
58132 $request = $client->put('/', array(), null, array('params' => array('foo' => 'test')));
58133 $this->assertEquals('test', $request->getParams()->get('foo'));
58134 }
58135
58136 public function testCanAddSslKey()
58137 {
58138 $client = new Client();
58139 $request = $client->get('/', array(), array('ssl_key' => '/foo.pem'));
58140 $this->assertEquals('/foo.pem', $request->getCurlOptions()->get(CURLOPT_SSLKEY));
58141 }
58142
58143 public function testCanAddSslKeyPassword()
58144 {
58145 $client = new Client();
58146 $request = $client->get('/', array(), array('ssl_key' => array('/foo.pem', 'bar')));
58147 $this->assertEquals('/foo.pem', $request->getCurlOptions()->get(CURLOPT_SSLKEY));
58148 $this->assertEquals('bar', $request->getCurlOptions()->get(CURLOPT_SSLKEYPASSWD));
58149 }
58150
58151 public function testCanAddSslCert()
58152 {
58153 $client = new Client();
58154 $request = $client->get('/', array(), array('cert' => '/foo.pem'));
58155 $this->assertEquals('/foo.pem', $request->getCurlOptions()->get(CURLOPT_SSLCERT));
58156 }
58157
58158 public function testCanAddSslCertPassword()
58159 {
58160 $client = new Client();
58161 $request = $client->get('/', array(), array('cert' => array('/foo.pem', 'bar')));
58162 $this->assertEquals('/foo.pem', $request->getCurlOptions()->get(CURLOPT_SSLCERT));
58163 $this->assertEquals('bar', $request->getCurlOptions()->get(CURLOPT_SSLCERTPASSWD));
58164 }
58165 }
58166 <?php
58167
58168 namespace Guzzle\Tests\Http\Message;
58169
58170 use Guzzle\Http\Message\Header;
58171 use Guzzle\Http\Message\Request;
58172 use Guzzle\Common\Collection;
58173
58174 /**
58175 * @covers Guzzle\Http\Message\AbstractMessage
58176 */
58177 class AbstractMessageTest extends \Guzzle\Tests\GuzzleTestCase
58178 {
58179 /** @var Request Request object */
58180 private $request;
58181
58182 /** @var AbstractMessage */
58183 private $mock;
58184
58185 public function setUp()
58186 {
58187 parent::setUp();
58188 $this->mock = $this->getMockForAbstractClass('Guzzle\Http\Message\AbstractMessage');
58189 }
58190
58191 public function tearDown()
58192 {
58193 $this->mock = $this->request = null;
58194 }
58195
58196 public function testGetParams()
58197 {
58198 $request = new Request('GET', 'http://example.com');
58199 $this->assertInstanceOf('Guzzle\\Common\\Collection', $request->getParams());
58200 }
58201
58202 public function testAddHeaders()
58203 {
58204 $this->mock->setHeader('A', 'B');
58205
58206 $this->assertEquals($this->mock, $this->mock->addHeaders(array(
58207 'X-Data' => '123'
58208 )));
58209
58210 $this->assertTrue($this->mock->hasHeader('X-Data') !== false);
58211 $this->assertTrue($this->mock->hasHeader('A') !== false);
58212 }
58213
58214 public function testAllowsHeaderToSetAsHeader()
58215 {
58216 $h = new Header('A', 'B');
58217 $this->mock->setHeader('A', $h);
58218 $this->assertSame($h, $this->mock->getHeader('A'));
58219 }
58220
58221 public function testGetHeader()
58222 {
58223 $this->mock->setHeader('Test', '123');
58224 $this->assertEquals('123', $this->mock->getHeader('Test'));
58225 }
58226
58227 public function testGetHeaders()
58228 {
58229 $this->assertSame($this->mock, $this->mock->setHeaders(array('a' => 'b', 'c' => 'd')));
58230 $h = $this->mock->getHeaders();
58231 $this->assertArrayHasKey('a', $h->toArray());
58232 $this->assertArrayHasKey('c', $h->toArray());
58233 $this->assertInstanceOf('Guzzle\Http\Message\Header\HeaderInterface', $h->get('a'));
58234 $this->assertInstanceOf('Guzzle\Http\Message\Header\HeaderInterface', $h->get('c'));
58235 }
58236
58237 public function testGetHeaderLinesUsesGlue()
58238 {
58239 $this->mock->setHeaders(array('a' => 'b', 'c' => 'd'));
58240 $this->mock->addHeader('a', 'e');
58241 $this->mock->getHeader('a')->setGlue('!');
58242 $this->assertEquals(array(
58243 'a: b! e',
58244 'c: d'
58245 ), $this->mock->getHeaderLines());
58246 }
58247
58248 public function testHasHeader()
58249 {
58250 $this->assertFalse($this->mock->hasHeader('Foo'));
58251 $this->mock->setHeader('Foo', 'Bar');
58252 $this->assertEquals(true, $this->mock->hasHeader('Foo'));
58253 $this->mock->setHeader('foo', 'yoo');
58254 $this->assertEquals(true, $this->mock->hasHeader('Foo'));
58255 $this->assertEquals(true, $this->mock->hasHeader('foo'));
58256 $this->assertEquals(false, $this->mock->hasHeader('bar'));
58257 }
58258
58259 public function testRemoveHeader()
58260 {
58261 $this->mock->setHeader('Foo', 'Bar');
58262 $this->assertEquals(true, $this->mock->hasHeader('Foo'));
58263 $this->mock->removeHeader('Foo');
58264 $this->assertFalse($this->mock->hasHeader('Foo'));
58265 }
58266
58267 public function testReturnsNullWhenHeaderIsNotFound()
58268 {
58269 $this->assertNull($this->mock->getHeader('foo'));
58270 }
58271
58272 public function testAddingHeadersPreservesOriginalHeaderCase()
58273 {
58274 $this->mock->addHeaders(array(
58275 'test' => '123',
58276 'Test' => 'abc'
58277 ));
58278 $this->mock->addHeader('test', '456');
58279 $this->mock->addHeader('test', '789');
58280
58281 $header = $this->mock->getHeader('test');
58282 $this->assertContains('123', $header->toArray());
58283 $this->assertContains('456', $header->toArray());
58284 $this->assertContains('789', $header->toArray());
58285 $this->assertContains('abc', $header->toArray());
58286 }
58287
58288 public function testCanStoreEmptyHeaders()
58289 {
58290 $this->mock->setHeader('Content-Length', 0);
58291 $this->assertTrue($this->mock->hasHeader('Content-Length'));
58292 $this->assertEquals(0, (string) $this->mock->getHeader('Content-Length'));
58293 }
58294
58295 public function testCanSetCustomHeaderFactory()
58296 {
58297 $f = new Header\HeaderFactory();
58298 $this->mock->setHeaderFactory($f);
58299 $this->assertSame($f, $this->readAttribute($this->mock, 'headerFactory'));
58300 }
58301 }
58302 <?php
58303
58304 namespace Guzzle\Tests\Http\Message;
58305
58306 use Guzzle\Http\Client;
58307 use Guzzle\Http\Message\PostFile;
58308
58309 /**
58310 * @covers Guzzle\Http\Message\PostFile
58311 * @group server
58312 */
58313 class PostFileTest extends \Guzzle\Tests\GuzzleTestCase
58314 {
58315 public function testConstructorConfiguresPostFile()
58316 {
58317 $file = new PostFile('foo', __FILE__, 'x-foo');
58318 $this->assertEquals('foo', $file->getFieldName());
58319 $this->assertEquals(__FILE__, $file->getFilename());
58320 $this->assertEquals('x-foo', $file->getContentType());
58321 }
58322
58323 public function testRemovesLeadingAtSymbolFromPath()
58324 {
58325 $file = new PostFile('foo', '@' . __FILE__);
58326 $this->assertEquals(__FILE__, $file->getFilename());
58327 }
58328
58329 /**
58330 * @expectedException Guzzle\Common\Exception\InvalidArgumentException
58331 */
58332 public function testEnsuresFileIsReadable()
58333 {
58334 $file = new PostFile('foo', '/foo/baz/bar');
58335 }
58336
58337 public function testCanChangeContentType()
58338 {
58339 $file = new PostFile('foo', '@' . __FILE__);
58340 $file->setContentType('Boo');
58341 $this->assertEquals('Boo', $file->getContentType());
58342 }
58343
58344 public function testCanChangeFieldName()
58345 {
58346 $file = new PostFile('foo', '@' . __FILE__);
58347 $file->setFieldName('Boo');
58348 $this->assertEquals('Boo', $file->getFieldName());
58349 }
58350
58351 public function testReturnsCurlValueString()
58352 {
58353 $file = new PostFile('foo', __FILE__);
58354 if (version_compare(phpversion(), '5.5.0', '<')) {
58355 $this->assertContains('@' . __FILE__ . ';filename=PostFileTest.php;type=text/x-', $file->getCurlValue());
58356 } else {
58357 $c = $file->getCurlValue();
58358 $this->assertEquals(__FILE__, $c->getFilename());
58359 $this->assertEquals('PostFileTest.php', $c->getPostFilename());
58360 $this->assertContains('text/x-', $c->getMimeType());
58361 }
58362 }
58363
58364 public function testContentDispositionFilePathIsStripped()
58365 {
58366 $this->getServer()->flush();
58367 $client = new Client($this->getServer()->getUrl());
58368 $this->getServer()->enqueue("HTTP/1.1 200 OK\r\nContent-Length: 0\r\n\r\n");
58369 $request = $client->post()->addPostFile('file', __FILE__);
58370 $request->send();
58371 $requests = $this->getServer()->getReceivedRequests(false);
58372 $this->assertContains('POST / HTTP/1.1', $requests[0]);
58373 $this->assertContains('Content-Disposition: form-data; name="file"; filename="PostFileTest.php"', $requests[0]);
58374 }
58375 }
58376 <?php
58377
58378 namespace Guzzle\Tests\Message;
58379
58380 use Guzzle\Common\Collection;
58381 use Guzzle\Tests\Http\Message\HeaderComparison;
58382
58383 class HeaderComparisonTest extends \Guzzle\Tests\GuzzleTestCase
58384 {
58385 public function filterProvider()
58386 {
58387 return array(
58388
58389 // Headers match
58390 array(array(
58391 'Content-Length' => 'Foo'
58392 ), array(
58393 'Content-Length' => 'Foo'
58394 ), false),
58395
58396 // Missing header
58397 array(array(
58398 'X-Foo' => 'Bar'
58399 ), array(), array(
58400 '- X-Foo' => 'Bar'
58401 )),
58402
58403 // Extra headers is present
58404 array(array(
58405 'X-Foo' => 'Bar'
58406 ), array(
58407 'X-Foo' => 'Bar',
58408 'X-Baz' => 'Jar'
58409 ), array(
58410 '+ X-Baz' => 'Jar'
58411 )),
58412
58413 // Header is present but must be absent
58414 array(array(
58415 '!X-Foo' => '*'
58416 ), array(
58417 'X-Foo' => 'Bar'
58418 ), array(
58419 '++ X-Foo' => 'Bar'
58420 )),
58421
58422 // Different values
58423 array(array(
58424 'X-Foo' => 'Bar'
58425 ), array(
58426 'X-Foo' => 'Baz'
58427 ), array(
58428 'X-Foo' => 'Baz != Bar'
58429 )),
58430
58431 // Wildcard search passes
58432 array(array(
58433 'X-Foo' => '*'
58434 ), array(
58435 'X-Foo' => 'Bar'
58436 ), false),
58437
58438 // Wildcard search fails
58439 array(array(
58440 'X-Foo' => '*'
58441 ), array(), array(
58442 '- X-Foo' => '*'
58443 )),
58444
58445 // Ignore extra header if present
58446 array(array(
58447 'X-Foo' => '*',
58448 '_X-Bar' => '*',
58449 ), array(
58450 'X-Foo' => 'Baz',
58451 'X-Bar' => 'Jar'
58452 ), false),
58453
58454 // Ignore extra header if present and is not
58455 array(array(
58456 'X-Foo' => '*',
58457 '_X-Bar' => '*',
58458 ), array(
58459 'X-Foo' => 'Baz'
58460 ), false),
58461
58462 // Case insensitive
58463 array(array(
58464 'X-Foo' => '*',
58465 '_X-Bar' => '*',
58466 ), array(
58467 'x-foo' => 'Baz',
58468 'x-BAR' => 'baz'
58469 ), false),
58470
58471 // Case insensitive with collection
58472 array(array(
58473 'X-Foo' => '*',
58474 '_X-Bar' => '*',
58475 ), new Collection(array(
58476 'x-foo' => 'Baz',
58477 'x-BAR' => 'baz'
58478 )), false),
58479 );
58480 }
58481
58482 /**
58483 * @dataProvider filterProvider
58484 */
58485 public function testComparesHeaders($filters, $headers, $result)
58486 {
58487 $compare = new HeaderComparison();
58488 $this->assertEquals($result, $compare->compare($filters, $headers));
58489 }
58490 }
58491 <?php
58492
58493 namespace Guzzle\Tests\Http\Message\Header;
58494
58495 use Guzzle\Http\Message\Header\Link;
58496 use Guzzle\Tests\GuzzleTestCase;
58497
58498 class LinkTest extends GuzzleTestCase
58499 {
58500 public function testParsesLinks()
58501 {
58502 $link = new Link('Link', '<http:/.../front.jpeg>; rel=front; type="image/jpeg", <http://.../back.jpeg>; rel=back; type="image/jpeg", <http://.../side.jpeg?test=1>; rel=side; type="image/jpeg"');
58503 $links = $link->getLinks();
58504 $this->assertEquals(array(
58505 array(
58506 'rel' => 'front',
58507 'type' => 'image/jpeg',
58508 'url' => 'http:/.../front.jpeg',
58509 ),
58510 array(
58511 'rel' => 'back',
58512 'type' => 'image/jpeg',
58513 'url' => 'http://.../back.jpeg',
58514 ),
58515 array(
58516 'rel' => 'side',
58517 'type' => 'image/jpeg',
58518 'url' => 'http://.../side.jpeg?test=1'
58519 )
58520 ), $links);
58521
58522 $this->assertEquals(array(
58523 'rel' => 'back',
58524 'type' => 'image/jpeg',
58525 'url' => 'http://.../back.jpeg',
58526 ), $link->getLink('back'));
58527
58528 $this->assertTrue($link->hasLink('front'));
58529 $this->assertFalse($link->hasLink('foo'));
58530 }
58531
58532 public function testCanAddLink()
58533 {
58534 $link = new Link('Link', '<http://foo>; rel=a; type="image/jpeg"');
58535 $link->addLink('http://test.com', 'test', array('foo' => 'bar'));
58536 $this->assertEquals(
58537 '<http://foo>; rel=a; type="image/jpeg", <http://test.com>; rel="test"; foo="bar"',
58538 (string) $link
58539 );
58540 }
58541
58542 public function testCanParseLinksWithCommas()
58543 {
58544 $link = new Link('Link', '<http://example.com/TheBook/chapter1>; rel="previous"; title="start, index"');
58545 $this->assertEquals(array(
58546 array(
58547 'rel' => 'previous',
58548 'title' => 'start, index',
58549 'url' => 'http://example.com/TheBook/chapter1',
58550 )
58551 ), $link->getLinks());
58552 }
58553 }
58554 <?php
58555
58556 namespace Guzzle\Tests\Http\Message\Header;
58557
58558 use Guzzle\Http\Message\Header\HeaderFactory;
58559
58560 /**
58561 * @covers Guzzle\Http\Message\Header\HeaderFactory
58562 */
58563 class HeaderFactoryTest extends \Guzzle\Tests\GuzzleTestCase
58564 {
58565 public function testCreatesBasicHeaders()
58566 {
58567 $f = new HeaderFactory();
58568 $h = $f->createHeader('Foo', 'Bar');
58569 $this->assertInstanceOf('Guzzle\Http\Message\Header', $h);
58570 $this->assertEquals('Foo', $h->getName());
58571 $this->assertEquals('Bar', (string) $h);
58572 }
58573
58574 public function testCreatesSpecificHeaders()
58575 {
58576 $f = new HeaderFactory();
58577 $h = $f->createHeader('Link', '<http>; rel="test"');
58578 $this->assertInstanceOf('Guzzle\Http\Message\Header\Link', $h);
58579 $this->assertEquals('Link', $h->getName());
58580 $this->assertEquals('<http>; rel="test"', (string) $h);
58581 }
58582 }
58583 <?php
58584
58585 namespace Guzzle\Tests\Http\Message;
58586
58587 use Guzzle\Http\Client;
58588 use Guzzle\Http\EntityBody;
58589 use Guzzle\Http\Message\Request;
58590 use Guzzle\Http\Message\RequestFactory;
58591 use Guzzle\Http\RedirectPlugin;
58592 use Guzzle\Http\Message\EntityEnclosingRequest;
58593 use Guzzle\Http\Message\PostFile;
58594 use Guzzle\Http\QueryString;
58595
58596 /**
58597 * @group server
58598 * @covers Guzzle\Http\Message\EntityEnclosingRequest
58599 */
58600 class EntityEnclosingRequestTest extends \Guzzle\Tests\GuzzleTestCase
58601 {
58602 protected $client;
58603
58604 public function setUp()
58605 {
58606 $this->client = new Client();
58607 }
58608
58609 public function tearDown()
58610 {
58611 $this->client = null;
58612 }
58613
58614 public function testConstructorConfiguresRequest()
58615 {
58616 $request = new EntityEnclosingRequest('PUT', 'http://test.com', array(
58617 'X-Test' => '123'
58618 ));
58619 $request->setBody('Test');
58620 $this->assertEquals('123', $request->getHeader('X-Test'));
58621 $this->assertNull($request->getHeader('Expect'));
58622 }
58623
58624 public function testCanSetBodyWithoutOverridingContentType()
58625 {
58626 $request = new EntityEnclosingRequest('PUT', 'http://test.com', array('Content-Type' => 'foooooo'));
58627 $request->setBody('{"a":"b"}');
58628 $this->assertEquals('foooooo', $request->getHeader('Content-Type'));
58629 }
58630
58631 public function testRequestIncludesBodyInMessage()
58632 {
58633
58634 $request = RequestFactory::getInstance()->create('PUT', 'http://www.guzzle-project.com/', null, 'data');
58635 $this->assertEquals("PUT / HTTP/1.1\r\n"
58636 . "Host: www.guzzle-project.com\r\n"
58637 . "Content-Length: 4\r\n\r\n"
58638 . "data", (string) $request);
58639 }
58640
58641 public function testRequestIncludesPostBodyInMessageOnlyWhenNoPostFiles()
58642 {
58643 $request = RequestFactory::getInstance()->create('POST', 'http://www.guzzle-project.com/', null, array(
58644 'foo' => 'bar'
58645 ));
58646 $this->assertEquals("POST / HTTP/1.1\r\n"
58647 . "Host: www.guzzle-project.com\r\n"
58648 . "Content-Type: application/x-www-form-urlencoded; charset=utf-8\r\n\r\n"
58649 . "foo=bar", (string) $request);
58650
58651 $request = RequestFactory::getInstance()->create('POST', 'http://www.guzzle-project.com/', null, array(
58652 'foo' => '@' . __FILE__
58653 ));
58654 $this->assertEquals("POST / HTTP/1.1\r\n"
58655 . "Host: www.guzzle-project.com\r\n"
58656 . "Content-Type: multipart/form-data\r\n"
58657 . "Expect: 100-Continue\r\n\r\n", (string) $request);
58658 }
58659
58660 public function testAddsPostFieldsAndSetsContentLength()
58661 {
58662 $request = RequestFactory::getInstance()->create('POST', 'http://www.guzzle-project.com/', null, array(
58663 'data' => '123'
58664 ));
58665 $this->assertEquals("POST / HTTP/1.1\r\n"
58666 . "Host: www.guzzle-project.com\r\n"
58667 . "Content-Type: application/x-www-form-urlencoded; charset=utf-8\r\n\r\n"
58668 . "data=123", (string) $request);
58669 }
58670
58671 public function testAddsPostFilesAndSetsContentType()
58672 {
58673 $request = RequestFactory::getInstance()->create('POST', 'http://www.test.com/')
58674 ->addPostFiles(array(
58675 'file' => __FILE__
58676 ))->addPostFields(array(
58677 'a' => 'b'
58678 ));
58679 $message = (string) $request;
58680 $this->assertEquals('multipart/form-data', $request->getHeader('Content-Type'));
58681 $this->assertEquals('100-Continue', $request->getHeader('Expect'));
58682 }
58683
58684 public function testRequestBodyContainsPostFiles()
58685 {
58686 $request = RequestFactory::getInstance()->create('POST', 'http://www.test.com/');
58687 $request->addPostFields(array(
58688 'test' => '123'
58689 ));
58690 $this->assertContains("\r\n\r\ntest=123", (string) $request);
58691 }
58692
58693 public function testRequestBodyAddsContentLength()
58694 {
58695 $request = RequestFactory::getInstance()->create('PUT', 'http://www.test.com/');
58696 $request->setBody(EntityBody::factory('test'));
58697 $this->assertEquals(4, (string) $request->getHeader('Content-Length'));
58698 $this->assertFalse($request->hasHeader('Transfer-Encoding'));
58699 }
58700
58701 public function testRequestBodyDoesNotUseContentLengthWhenChunked()
58702 {
58703 $request = RequestFactory::getInstance()->create('PUT', 'http://www.test.com/', array(
58704 'Transfer-Encoding' => 'chunked'
58705 ), 'test');
58706 $this->assertNull($request->getHeader('Content-Length'));
58707 $this->assertTrue($request->hasHeader('Transfer-Encoding'));
58708 }
58709
58710 public function testRequestHasMutableBody()
58711 {
58712 $request = RequestFactory::getInstance()->create('PUT', 'http://www.guzzle-project.com/', null, 'data');
58713 $body = $request->getBody();
58714 $this->assertInstanceOf('Guzzle\\Http\\EntityBody', $body);
58715 $this->assertSame($body, $request->getBody());
58716
58717 $newBody = EntityBody::factory('foobar');
58718 $request->setBody($newBody);
58719 $this->assertEquals('foobar', (string) $request->getBody());
58720 $this->assertSame($newBody, $request->getBody());
58721 }
58722
58723 public function testSetPostFields()
58724 {
58725 $request = RequestFactory::getInstance()->create('POST', 'http://www.guzzle-project.com/');
58726 $this->assertInstanceOf('Guzzle\\Http\\QueryString', $request->getPostFields());
58727
58728 $fields = new QueryString(array(
58729 'a' => 'b'
58730 ));
58731 $request->addPostFields($fields);
58732 $this->assertEquals($fields->getAll(), $request->getPostFields()->getAll());
58733 $this->assertEquals(array(), $request->getPostFiles());
58734 }
58735
58736 public function testSetPostFiles()
58737 {
58738 $request = RequestFactory::getInstance()->create('POST', $this->getServer()->getUrl())
58739 ->setClient(new Client())
58740 ->addPostFiles(array(__FILE__))
58741 ->addPostFields(array(
58742 'test' => 'abc'
58743 ));
58744
58745 $request->getCurlOptions()->set('debug', true);
58746
58747 $this->assertEquals(array(
58748 'test' => 'abc'
58749 ), $request->getPostFields()->getAll());
58750
58751 $files = $request->getPostFiles();
58752 $post = $files['file'][0];
58753 $this->assertEquals('file', $post->getFieldName());
58754 $this->assertContains('text/x-', $post->getContentType());
58755 $this->assertEquals(__FILE__, $post->getFilename());
58756
58757 $this->getServer()->enqueue("HTTP/1.1 200 OK\r\nContent-Length: 0\r\n\r\n");
58758 $request->send();
58759
58760 $this->assertNotNull($request->getHeader('Content-Length'));
58761 $this->assertContains('multipart/form-data; boundary=', (string) $request->getHeader('Content-Type'), '-> cURL must add the boundary');
58762 }
58763
58764 /**
58765 * @expectedException Guzzle\Common\Exception\InvalidArgumentException
58766 */
58767 public function testSetPostFilesThrowsExceptionWhenFileIsNotFound()
58768 {
58769 $request = RequestFactory::getInstance()->create('POST', 'http://www.guzzle-project.com/')
58770 ->addPostFiles(array(
58771 'file' => 'filenotfound.ini'
58772 ));
58773 }
58774
58775 /**
58776 * @expectedException Guzzle\Http\Exception\RequestException
58777 */
58778 public function testThrowsExceptionWhenNonStringsAreAddedToPost()
58779 {
58780 $request = RequestFactory::getInstance()->create('POST', 'http://www.guzzle-project.com/')
58781 ->addPostFile('foo', new \stdClass());
58782 }
58783
58784 public function testAllowsContentTypeInPostUploads()
58785 {
58786 $request = RequestFactory::getInstance()->create('POST', 'http://www.guzzle-project.com/')
58787 ->addPostFile('foo', __FILE__, 'text/plain');
58788
58789 $this->assertEquals(array(
58790 new PostFile('foo', __FILE__, 'text/plain')
58791 ), $request->getPostFile('foo'));
58792 }
58793
58794 public function testGuessesContentTypeOfPostUpload()
58795 {
58796 $request = RequestFactory::getInstance()->create('POST', 'http://www.guzzle-project.com/')
58797 ->addPostFile('foo', __FILE__);
58798 $file = $request->getPostFile('foo');
58799 $this->assertContains('text/x-', $file[0]->getContentType());
58800 }
58801
58802 public function testAllowsContentDispositionFieldsInPostUploadsWhenSettingInBulk()
58803 {
58804 $postFile = new PostFile('foo', __FILE__, 'text/x-php');
58805 $request = RequestFactory::getInstance()->create('POST', 'http://www.guzzle-project.com/')
58806 ->addPostFiles(array('foo' => $postFile));
58807
58808 $this->assertEquals(array($postFile), $request->getPostFile('foo'));
58809 }
58810
58811 public function testPostRequestsUseApplicationXwwwForUrlEncodedForArrays()
58812 {
58813 $request = RequestFactory::getInstance()->create('POST', 'http://www.guzzle-project.com/');
58814 $request->setPostField('a', 'b');
58815 $this->assertContains("\r\n\r\na=b", (string) $request);
58816 $this->assertEquals('application/x-www-form-urlencoded; charset=utf-8', $request->getHeader('Content-Type'));
58817 }
58818
58819 public function testProcessMethodAddsContentType()
58820 {
58821 $request = RequestFactory::getInstance()->create('POST', 'http://www.guzzle-project.com/');
58822 $request->setPostField('a', 'b');
58823 $this->assertEquals('application/x-www-form-urlencoded; charset=utf-8', $request->getHeader('Content-Type'));
58824 }
58825
58826 public function testPostRequestsUseMultipartFormDataWithFiles()
58827 {
58828 $request = RequestFactory::getInstance()->create('POST', 'http://www.guzzle-project.com/');
58829 $request->addPostFiles(array('file' => __FILE__));
58830 $this->assertEquals('multipart/form-data', $request->getHeader('Content-Type'));
58831 }
58832
58833 public function testCanSendMultipleRequestsUsingASingleRequestObject()
58834 {
58835 $this->getServer()->flush();
58836 $this->getServer()->enqueue(array(
58837 "HTTP/1.1 200 OK\r\nContent-Length: 0\r\n\r\n",
58838 "HTTP/1.1 201 Created\r\nContent-Length: 0\r\n\r\n",
58839 ));
58840
58841 // Send the first request
58842 $request = RequestFactory::getInstance()->create('PUT', $this->getServer()->getUrl())
58843 ->setBody('test')
58844 ->setClient(new Client());
58845 $request->send();
58846 $this->assertEquals(200, $request->getResponse()->getStatusCode());
58847
58848 // Send the second request
58849 $request->setBody('abcdefg', 'application/json', false);
58850 $request->send();
58851 $this->assertEquals(201, $request->getResponse()->getStatusCode());
58852
58853 // Ensure that the same request was sent twice with different bodies
58854 $requests = $this->getServer()->getReceivedRequests(true);
58855 $this->assertEquals(2, count($requests));
58856 $this->assertEquals(4, (string) $requests[0]->getHeader('Content-Length'));
58857 $this->assertEquals(7, (string) $requests[1]->getHeader('Content-Length'));
58858 }
58859
58860 public function testRemovingPostFieldRebuildsPostFields()
58861 {
58862 $request = new EntityEnclosingRequest('POST', 'http://test.com');
58863 $request->setPostField('test', 'value');
58864 $request->removePostField('test');
58865 $this->assertNull($request->getPostField('test'));
58866 }
58867
58868 public function testUsesChunkedTransferWhenBodyLengthCannotBeDetermined()
58869 {
58870 $this->getServer()->enqueue("HTTP/1.1 200 OK\r\nContent-Length: 0\r\n\r\n");
58871 $request = new EntityEnclosingRequest('PUT', 'http://test.com/');
58872 $request->setBody(fopen($this->getServer()->getUrl(), 'r'));
58873 $this->assertEquals('chunked', $request->getHeader('Transfer-Encoding'));
58874 $this->assertFalse($request->hasHeader('Content-Length'));
58875 }
58876
58877 /**
58878 * @expectedException \Guzzle\Http\Exception\RequestException
58879 */
58880 public function testThrowsExceptionWhenContentLengthCannotBeDeterminedAndUsingHttp1()
58881 {
58882 $request = new EntityEnclosingRequest('PUT', 'http://test.com/');
58883 $this->getServer()->enqueue("HTTP/1.1 200 OK\r\nContent-Length: 0\r\n\r\n");
58884 $request->setProtocolVersion('1.0');
58885 $request->setBody(fopen($this->getServer()->getUrl(), 'r'));
58886 }
58887
58888 public function testAllowsNestedPostData()
58889 {
58890 $request = new EntityEnclosingRequest('POST', 'http://test.com/');
58891 $request->addPostFields(array(
58892 'a' => array('b', 'c')
58893 ));
58894 $this->assertEquals(array(
58895 'a' => array('b', 'c')
58896 ), $request->getPostFields()->getAll());
58897 }
58898
58899 public function testAllowsEmptyFields()
58900 {
58901 $request = new EntityEnclosingRequest('POST', 'http://test.com/');
58902 $request->addPostFields(array(
58903 'a' => ''
58904 ));
58905 $this->assertEquals(array(
58906 'a' => ''
58907 ), $request->getPostFields()->getAll());
58908 }
58909
58910 /**
58911 * @expectedException \Guzzle\Http\Exception\RequestException
58912 */
58913 public function testFailsOnInvalidFiles()
58914 {
58915 $request = new EntityEnclosingRequest('POST', 'http://test.com/');
58916 $request->addPostFiles(array(
58917 'a' => new \stdClass()
58918 ));
58919 }
58920
58921 public function testHandlesEmptyStrings()
58922 {
58923 $request = new EntityEnclosingRequest('POST', 'http://test.com/');
58924 $request->addPostFields(array(
58925 'a' => '',
58926 'b' => null,
58927 'c' => 'Foo'
58928 ));
58929 $this->assertEquals(array(
58930 'a' => '',
58931 'b' => null,
58932 'c' => 'Foo'
58933 ), $request->getPostFields()->getAll());
58934 }
58935
58936 public function testHoldsPostFiles()
58937 {
58938 $request = new EntityEnclosingRequest('POST', 'http://test.com/');
58939 $request->addPostFile('foo', __FILE__);
58940 $request->addPostFile(new PostFile('foo', __FILE__));
58941
58942 $this->assertArrayHasKey('foo', $request->getPostFiles());
58943 $foo = $request->getPostFile('foo');
58944 $this->assertEquals(2, count($foo));
58945 $this->assertEquals(__FILE__, $foo[0]->getFilename());
58946 $this->assertEquals(__FILE__, $foo[1]->getFilename());
58947
58948 $request->removePostFile('foo');
58949 $this->assertEquals(array(), $request->getPostFiles());
58950 }
58951
58952 public function testAllowsAtPrefixWhenAddingPostFiles()
58953 {
58954 $request = new EntityEnclosingRequest('POST', 'http://test.com/');
58955 $request->addPostFiles(array(
58956 'foo' => '@' . __FILE__
58957 ));
58958 $foo = $request->getPostFile('foo');
58959 $this->assertEquals(__FILE__, $foo[0]->getFilename());
58960 }
58961
58962 public function testSetStateToTransferWithEmptyBodySetsContentLengthToZero()
58963 {
58964 $request = new EntityEnclosingRequest('POST', 'http://test.com/');
58965 $request->setState($request::STATE_TRANSFER);
58966 $this->assertEquals('0', (string) $request->getHeader('Content-Length'));
58967 }
58968
58969 public function testSettingExpectHeaderCutoffChangesRequest()
58970 {
58971 $request = new EntityEnclosingRequest('PUT', 'http://test.com/');
58972 $request->setHeader('Expect', '100-Continue');
58973 $request->setExpectHeaderCutoff(false);
58974 $this->assertNull($request->getHeader('Expect'));
58975 // There is not body, so remove the expect header
58976 $request->setHeader('Expect', '100-Continue');
58977 $request->setExpectHeaderCutoff(10);
58978 $this->assertNull($request->getHeader('Expect'));
58979 // The size is less than the cutoff
58980 $request->setBody('foo');
58981 $this->assertNull($request->getHeader('Expect'));
58982 // The size is greater than the cutoff
58983 $request->setBody('foobazbarbamboo');
58984 $this->assertNotNull($request->getHeader('Expect'));
58985 }
58986
58987 public function testStrictRedirectsCanBeSpecifiedOnEntityEnclosingRequests()
58988 {
58989 $request = new EntityEnclosingRequest('PUT', 'http://test.com/');
58990 $request->configureRedirects(true);
58991 $this->assertTrue($request->getParams()->get(RedirectPlugin::STRICT_REDIRECTS));
58992 }
58993
58994 public function testCanDisableRedirects()
58995 {
58996 $request = new EntityEnclosingRequest('PUT', 'http://test.com/');
58997 $request->configureRedirects(false, false);
58998 $this->assertTrue($request->getParams()->get(RedirectPlugin::DISABLE));
58999 }
59000
59001 public function testSetsContentTypeWhenSettingBodyByGuessingFromEntityBody()
59002 {
59003 $request = new EntityEnclosingRequest('PUT', 'http://test.com/foo');
59004 $request->setBody(EntityBody::factory(fopen(__FILE__, 'r')));
59005 $this->assertEquals('text/x-php', (string) $request->getHeader('Content-Type'));
59006 }
59007
59008 public function testDoesNotCloneBody()
59009 {
59010 $request = new EntityEnclosingRequest('PUT', 'http://test.com/foo');
59011 $request->setBody('test');
59012 $newRequest = clone $request;
59013 $newRequest->setBody('foo');
59014 $this->assertInternalType('string', (string) $request->getBody());
59015 }
59016 }
59017 <?php
59018
59019 namespace Guzzle\Tests\Http\Message;
59020
59021 use Guzzle\Http\Message\Header;
59022 use Guzzle\Http\Message\Response;
59023
59024 /**
59025 * @covers Guzzle\Http\Message\Header
59026 */
59027 class HeaderTest extends \Guzzle\Tests\GuzzleTestCase
59028 {
59029 protected $test = array(
59030 'zoo' => array('foo', 'Foo'),
59031 'Zoo' => 'bar',
59032 );
59033
59034 public function testStoresHeaderName()
59035 {
59036 $i = new Header('Zoo', $this->test);
59037 $this->assertEquals('Zoo', $i->getName());
59038 }
59039
59040 public function testConvertsToString()
59041 {
59042 $i = new Header('Zoo', $this->test);
59043 $this->assertEquals('foo, Foo, bar', (string) $i);
59044 $i->setGlue(';');
59045 $this->assertEquals('foo; Foo; bar', (string) $i);
59046 }
59047
59048 public function testNormalizesGluedHeaders()
59049 {
59050 $h = new Header('Zoo', array('foo, Faz', 'bar'));
59051 $result = $h->normalize(true)->toArray();
59052 natsort($result);
59053 $this->assertEquals(array('bar', 'foo', 'Faz'), $result);
59054 }
59055
59056 public function testCanSearchForValues()
59057 {
59058 $h = new Header('Zoo', $this->test);
59059 $this->assertTrue($h->hasValue('foo'));
59060 $this->assertTrue($h->hasValue('Foo'));
59061 $this->assertTrue($h->hasValue('bar'));
59062 $this->assertFalse($h->hasValue('moo'));
59063 $this->assertFalse($h->hasValue('FoO'));
59064 }
59065
59066 public function testIsCountable()
59067 {
59068 $h = new Header('Zoo', $this->test);
59069 $this->assertEquals(3, count($h));
59070 }
59071
59072 public function testCanBeIterated()
59073 {
59074 $h = new Header('Zoo', $this->test);
59075 $results = array();
59076 foreach ($h as $key => $value) {
59077 $results[$key] = $value;
59078 }
59079 $this->assertEquals(array(
59080 'foo', 'Foo', 'bar'
59081 ), $results);
59082 }
59083
59084 public function testAllowsFalseyValues()
59085 {
59086 // Allows 0
59087 $h = new Header('Foo', 0, ';');
59088 $this->assertEquals('0', (string) $h);
59089 $this->assertEquals(1, count($h));
59090 $this->assertEquals(';', $h->getGlue());
59091
59092 // Does not add a null header by default
59093 $h = new Header('Foo');
59094 $this->assertEquals('', (string) $h);
59095 $this->assertEquals(0, count($h));
59096
59097 // Allows null array for a single null header
59098 $h = new Header('Foo', array(null));
59099 $this->assertEquals('', (string) $h);
59100
59101 // Allows empty string
59102 $h = new Header('Foo', '');
59103 $this->assertEquals('', (string) $h);
59104 $this->assertEquals(1, count($h));
59105 }
59106
59107 public function testCanRemoveValues()
59108 {
59109 $h = new Header('Foo', array('Foo', 'baz', 'bar'));
59110 $h->removeValue('bar');
59111 $this->assertTrue($h->hasValue('Foo'));
59112 $this->assertFalse($h->hasValue('bar'));
59113 $this->assertTrue($h->hasValue('baz'));
59114 }
59115
59116 public function testAllowsArrayInConstructor()
59117 {
59118 $h = new Header('Foo', array('Testing', '123', 'Foo=baz'));
59119 $this->assertEquals(array('Testing', '123', 'Foo=baz'), $h->toArray());
59120 }
59121
59122 public function parseParamsProvider()
59123 {
59124 $res1 = array(
59125 array(
59126 '<http:/.../front.jpeg>' => '',
59127 'rel' => 'front',
59128 'type' => 'image/jpeg',
59129 ),
59130 array(
59131 '<http://.../back.jpeg>' => '',
59132 'rel' => 'back',
59133 'type' => 'image/jpeg',
59134 ),
59135 );
59136
59137 return array(
59138 array(
59139 '<http:/.../front.jpeg>; rel="front"; type="image/jpeg", <http://.../back.jpeg>; rel=back; type="image/jpeg"',
59140 $res1
59141 ),
59142 array(
59143 '<http:/.../front.jpeg>; rel="front"; type="image/jpeg",<http://.../back.jpeg>; rel=back; type="image/jpeg"',
59144 $res1
59145 ),
59146 array(
59147 'foo="baz"; bar=123, boo, test="123", foobar="foo;bar"',
59148 array(
59149 array('foo' => 'baz', 'bar' => '123'),
59150 array('boo' => ''),
59151 array('test' => '123'),
59152 array('foobar' => 'foo;bar')
59153 )
59154 ),
59155 array(
59156 '<http://.../side.jpeg?test=1>; rel="side"; type="image/jpeg",<http://.../side.jpeg?test=2>; rel=side; type="image/jpeg"',
59157 array(
59158 array('<http://.../side.jpeg?test=1>' => '', 'rel' => 'side', 'type' => 'image/jpeg'),
59159 array('<http://.../side.jpeg?test=2>' => '', 'rel' => 'side', 'type' => 'image/jpeg')
59160 )
59161 )
59162 );
59163 }
59164
59165 /**
59166 * @dataProvider parseParamsProvider
59167 */
59168 public function testParseParams($header, $result)
59169 {
59170 $response = new Response(200, array('Link' => $header));
59171 $this->assertEquals($result, $response->getHeader('Link')->parseParams());
59172 }
59173 }
59174 <?php
59175
59176 namespace Guzzle\Tests\Http\Message;
59177
59178 use Guzzle\Common\Collection;
59179 use Guzzle\Http\Message\Header\HeaderCollection;
59180
59181 /**
59182 * Class used to compare HTTP headers using a custom DSL
59183 */
59184 class HeaderComparison
59185 {
59186 /**
59187 * Compare HTTP headers and use special markup to filter values
59188 * A header prefixed with '!' means it must not exist
59189 * A header prefixed with '_' means it must be ignored
59190 * A header value of '*' means anything after the * will be ignored
59191 *
59192 * @param array $filteredHeaders Array of special headers
59193 * @param array $actualHeaders Array of headers to check against
59194 *
59195 * @return array|bool Returns an array of the differences or FALSE if none
59196 */
59197 public function compare($filteredHeaders, $actualHeaders)
59198 {
59199 $expected = array();
59200 $ignore = array();
59201 $absent = array();
59202
59203 if ($actualHeaders instanceof HeaderCollection) {
59204 $actualHeaders = $actualHeaders->toArray();
59205 }
59206
59207 foreach ($filteredHeaders as $k => $v) {
59208 if ($k[0] == '_') {
59209 // This header should be ignored
59210 $ignore[] = str_replace('_', '', $k);
59211 } elseif ($k[0] == '!') {
59212 // This header must not be present
59213 $absent[] = str_replace('!', '', $k);
59214 } else {
59215 $expected[$k] = $v;
59216 }
59217 }
59218
59219 return $this->compareArray($expected, $actualHeaders, $ignore, $absent);
59220 }
59221
59222 /**
59223 * Check if an array of HTTP headers matches another array of HTTP headers while taking * into account as a wildcard
59224 *
59225 * @param array $expected Expected HTTP headers (allows wildcard values)
59226 * @param array|Collection $actual Actual HTTP header array
59227 * @param array $ignore Headers to ignore from the comparison
59228 * @param array $absent Array of headers that must not be present
59229 *
59230 * @return array|bool Returns an array of the differences or FALSE if none
59231 */
59232 public function compareArray(array $expected, $actual, array $ignore = array(), array $absent = array())
59233 {
59234 $differences = array();
59235
59236 // Add information about headers that were present but weren't supposed to be
59237 foreach ($absent as $header) {
59238 if ($this->hasKey($header, $actual)) {
59239 $differences["++ {$header}"] = $actual[$header];
59240 unset($actual[$header]);
59241 }
59242 }
59243
59244 // Check if expected headers are missing
59245 foreach ($expected as $header => $value) {
59246 if (!$this->hasKey($header, $actual)) {
59247 $differences["- {$header}"] = $value;
59248 }
59249 }
59250
59251 // Flip the ignore array so it works with the case insensitive helper
59252 $ignore = array_flip($ignore);
59253 // Allow case-insensitive comparisons in wildcards
59254 $expected = array_change_key_case($expected);
59255
59256 // Compare the expected and actual HTTP headers in no particular order
59257 foreach ($actual as $key => $value) {
59258
59259 // If this is to be ignored, the skip it
59260 if ($this->hasKey($key, $ignore)) {
59261 continue;
59262 }
59263
59264 // If the header was not expected
59265 if (!$this->hasKey($key, $expected)) {
59266 $differences["+ {$key}"] = $value;
59267 continue;
59268 }
59269
59270 // Check values and take wildcards into account
59271 $lkey = strtolower($key);
59272 $pos = is_string($expected[$lkey]) ? strpos($expected[$lkey], '*') : false;
59273
59274 foreach ((array) $actual[$key] as $v) {
59275 if (($pos === false && $v != $expected[$lkey]) || $pos > 0 && substr($v, 0, $pos) != substr($expected[$lkey], 0, $pos)) {
59276 $differences[$key] = "{$value} != {$expected[$lkey]}";
59277 }
59278 }
59279 }
59280
59281 return empty($differences) ? false : $differences;
59282 }
59283
59284 /**
59285 * Case insensitive check if an array have a key
59286 *
59287 * @param string $key Key to check
59288 * @param array $array Array to check
59289 *
59290 * @return bool
59291 */
59292 protected function hasKey($key, $array)
59293 {
59294 if ($array instanceof Collection) {
59295 $keys = $array->getKeys();
59296 } else {
59297 $keys = array_keys($array);
59298 }
59299
59300 foreach ($keys as $k) {
59301 if (!strcasecmp($k, $key)) {
59302 return true;
59303 }
59304 }
59305
59306 return false;
59307 }
59308 }
59309 <?php
59310
59311 namespace Guzzle\Tests\Http\Message;
59312
59313 use Guzzle\Common\Collection;
59314 use Guzzle\Http\EntityBody;
59315 use Guzzle\Http\Url;
59316 use Guzzle\Http\Client;
59317 use Guzzle\Plugin\Async\AsyncPlugin;
59318 use Guzzle\Http\Message\RequestInterface;
59319 use Guzzle\Http\Message\Request;
59320 use Guzzle\Http\Message\Response;
59321 use Guzzle\Http\Message\RequestFactory;
59322 use Guzzle\Http\RedirectPlugin;
59323 use Guzzle\Http\Exception\BadResponseException;
59324
59325 /**
59326 * @group server
59327 * @covers Guzzle\Http\Message\Request
59328 * @covers Guzzle\Http\Message\AbstractMessage
59329 */
59330 class RequestTest extends \Guzzle\Tests\GuzzleTestCase
59331 {
59332 /** @var Request */
59333 protected $request;
59334
59335 /** @var Client */
59336 protected $client;
59337
59338 protected function setUp()
59339 {
59340 $this->client = new Client($this->getServer()->getUrl());
59341 $this->request = $this->client->get();
59342 }
59343
59344 public function tearDown()
59345 {
59346 unset($this->request);
59347 unset($this->client);
59348 }
59349
59350 public function testConstructorBuildsRequestWithArrayHeaders()
59351 {
59352 // Test passing an array of headers
59353 $request = new Request('GET', 'http://www.guzzle-project.com/', array(
59354 'foo' => 'bar'
59355 ));
59356
59357 $this->assertEquals('GET', $request->getMethod());
59358 $this->assertEquals('http://www.guzzle-project.com/', $request->getUrl());
59359 $this->assertEquals('bar', $request->getHeader('foo'));
59360 }
59361
59362 public function testDescribesEvents()
59363 {
59364 $this->assertInternalType('array', Request::getAllEvents());
59365 }
59366
59367 public function testConstructorBuildsRequestWithCollectionHeaders()
59368 {
59369 $request = new Request('GET', 'http://www.guzzle-project.com/', new Collection(array(
59370 'foo' => 'bar'
59371 )));
59372 $this->assertEquals('bar', $request->getHeader('foo'));
59373 }
59374
59375 public function testConstructorBuildsRequestWithNoHeaders()
59376 {
59377 $request = new Request('GET', 'http://www.guzzle-project.com/', null);
59378 $this->assertFalse($request->hasHeader('foo'));
59379 }
59380
59381 public function testConstructorHandlesNonBasicAuth()
59382 {
59383 $request = new Request('GET', 'http://www.guzzle-project.com/', array(
59384 'Authorization' => 'Foo bar'
59385 ));
59386 $this->assertNull($request->getUserName());
59387 $this->assertNull($request->getPassword());
59388 $this->assertEquals('Foo bar', (string) $request->getHeader('Authorization'));
59389 }
59390
59391 public function testRequestsCanBeConvertedToRawMessageStrings()
59392 {
59393 $auth = base64_encode('michael:123');
59394 $message = "PUT /path?q=1&v=2 HTTP/1.1\r\n"
59395 . "Host: www.google.com\r\n"
59396 . "Authorization: Basic {$auth}\r\n"
59397 . "Content-Length: 4\r\n\r\nData";
59398
59399 $request = RequestFactory::getInstance()->create('PUT', 'http://www.google.com/path?q=1&v=2', array(
59400 'Authorization' => 'Basic ' . $auth
59401 ), 'Data');
59402
59403 $this->assertEquals($message, $request->__toString());
59404 }
59405
59406 /**
59407 * Add authorization after the fact and see that it was put in the message
59408 */
59409 public function testRequestStringsIncludeAuth()
59410 {
59411 $auth = base64_encode('michael:123');
59412 $this->getServer()->enqueue("HTTP/1.1 200 OK\r\nContent-Length: 0\r\n\r\n");
59413 $request = RequestFactory::getInstance()->create('PUT', $this->getServer()->getUrl(), null, 'Data')
59414 ->setClient($this->client)
59415 ->setAuth('michael', '123', CURLAUTH_BASIC);
59416 $request->send();
59417
59418 $this->assertContains('Authorization: Basic ' . $auth, (string) $request);
59419 }
59420
59421 public function testGetEventDispatcher()
59422 {
59423 $d = $this->request->getEventDispatcher();
59424 $this->assertInstanceOf('Symfony\\Component\\EventDispatcher\\EventDispatcherInterface', $d);
59425 $this->assertEquals($d, $this->request->getEventDispatcher());
59426 }
59427
59428 public function testRequestsManageClients()
59429 {
59430 $request = new Request('GET', 'http://test.com');
59431 $this->assertNull($request->getClient());
59432 $request->setClient($this->client);
59433 $this->assertSame($this->client, $request->getClient());
59434 }
59435
59436 /**
59437 * @expectedException \RuntimeException
59438 * @expectedExceptionMessage A client must be set on the request
59439 */
59440 public function testRequestsRequireClients()
59441 {
59442 $request = new Request('GET', 'http://test.com');
59443 $request->send();
59444 }
59445
59446 public function testSend()
59447 {
59448 $response = new Response(200, array(
59449 'Content-Length' => 3
59450 ), 'abc');
59451 $this->request->setResponse($response, true);
59452 $r = $this->request->send();
59453
59454 $this->assertSame($response, $r);
59455 $this->assertInstanceOf('Guzzle\\Http\\Message\\Response', $this->request->getResponse());
59456 $this->assertSame($r, $this->request->getResponse());
59457 $this->assertEquals('complete', $this->request->getState());
59458 }
59459
59460 public function testGetResponse()
59461 {
59462 $this->assertNull($this->request->getResponse());
59463 $response = new Response(200, array('Content-Length' => 3), 'abc');
59464
59465 $this->request->setResponse($response);
59466 $this->assertEquals($response, $this->request->getResponse());
59467
59468 $client = new Client('http://www.google.com');
59469 $request = $client->get('http://www.google.com/');
59470 $request->setResponse($response, true);
59471 $request->send();
59472 $requestResponse = $request->getResponse();
59473 $this->assertSame($response, $requestResponse);
59474
59475 // Try again, making sure it's still the same response
59476 $this->assertSame($requestResponse, $request->getResponse());
59477
59478 $response = new Response(204);
59479 $request = $client->get();
59480 $request->setResponse($response, true);
59481 $request->send();
59482 $requestResponse = $request->getResponse();
59483 $this->assertSame($response, $requestResponse);
59484 $this->assertInstanceOf('Guzzle\\Http\\EntityBody', $response->getBody());
59485 }
59486
59487 public function testRequestThrowsExceptionOnBadResponse()
59488 {
59489 try {
59490 $this->request->setResponse(new Response(404, array('Content-Length' => 3), 'abc'), true);
59491 $this->request->send();
59492 $this->fail('Expected exception not thrown');
59493 } catch (BadResponseException $e) {
59494 $this->assertInstanceOf('Guzzle\\Http\\Message\\RequestInterface', $e->getRequest());
59495 $this->assertInstanceOf('Guzzle\\Http\\Message\\Response', $e->getResponse());
59496 $this->assertContains('Client error response', $e->getMessage());
59497 }
59498 }
59499
59500 public function testManagesQuery()
59501 {
59502 $this->assertInstanceOf('Guzzle\\Http\\QueryString', $this->request->getQuery());
59503 $this->request->getQuery()->set('test', '123');
59504 $this->assertEquals('test=123', $this->request->getQuery(true));
59505 }
59506
59507 public function testRequestHasMethod()
59508 {
59509 $this->assertEquals('GET', $this->request->getMethod());
59510 }
59511
59512 public function testRequestHasScheme()
59513 {
59514 $this->assertEquals('http', $this->request->getScheme());
59515 $this->assertEquals($this->request, $this->request->setScheme('https'));
59516 $this->assertEquals('https', $this->request->getScheme());
59517 }
59518
59519 public function testRequestHasHost()
59520 {
59521 $this->assertEquals('127.0.0.1', $this->request->getHost());
59522 $this->assertEquals('127.0.0.1:8124', (string) $this->request->getHeader('Host'));
59523
59524 $this->assertSame($this->request, $this->request->setHost('www2.google.com'));
59525 $this->assertEquals('www2.google.com', $this->request->getHost());
59526 $this->assertEquals('www2.google.com:8124', (string) $this->request->getHeader('Host'));
59527
59528 $this->assertSame($this->request, $this->request->setHost('www.test.com:8081'));
59529 $this->assertEquals('www.test.com', $this->request->getHost());
59530 $this->assertEquals(8081, $this->request->getPort());
59531 }
59532
59533 public function testRequestHasProtocol()
59534 {
59535 $this->assertEquals('1.1', $this->request->getProtocolVersion());
59536 $this->assertEquals($this->request, $this->request->setProtocolVersion('1.1'));
59537 $this->assertEquals('1.1', $this->request->getProtocolVersion());
59538 $this->assertEquals($this->request, $this->request->setProtocolVersion('1.0'));
59539 $this->assertEquals('1.0', $this->request->getProtocolVersion());
59540 }
59541
59542 public function testRequestHasPath()
59543 {
59544 $this->assertEquals('/', $this->request->getPath());
59545 $this->assertEquals($this->request, $this->request->setPath('/index.html'));
59546 $this->assertEquals('/index.html', $this->request->getPath());
59547 $this->assertEquals($this->request, $this->request->setPath('index.html'));
59548 $this->assertEquals('/index.html', $this->request->getPath());
59549 }
59550
59551 public function testPermitsFalsyComponents()
59552 {
59553 $request = new Request('GET', 'http://0/0?0');
59554 $this->assertSame('0', $request->getHost());
59555 $this->assertSame('/0', $request->getPath());
59556 $this->assertSame('0=', $request->getQuery(true));
59557
59558 $request = new Request('GET', '0');
59559 $this->assertEquals('/0', $request->getPath());
59560 }
59561
59562 public function testRequestHasPort()
59563 {
59564 $this->assertEquals(8124, $this->request->getPort());
59565 $this->assertEquals('127.0.0.1:8124', $this->request->getHeader('Host'));
59566
59567 $this->assertEquals($this->request, $this->request->setPort('8080'));
59568 $this->assertEquals('8080', $this->request->getPort());
59569 $this->assertEquals('127.0.0.1:8080', $this->request->getHeader('Host'));
59570
59571 $this->request->setPort(80);
59572 $this->assertEquals('127.0.0.1', $this->request->getHeader('Host'));
59573 }
59574
59575 public function testRequestHandlesAuthorization()
59576 {
59577 // Uninitialized auth
59578 $this->assertEquals(null, $this->request->getUsername());
59579 $this->assertEquals(null, $this->request->getPassword());
59580
59581 // Set an auth
59582 $this->assertSame($this->request, $this->request->setAuth('michael', '123'));
59583 $this->assertEquals('michael', $this->request->getUsername());
59584 $this->assertEquals('123', $this->request->getPassword());
59585
59586 // Set an auth with blank password
59587 $this->assertSame($this->request, $this->request->setAuth('michael', ''));
59588 $this->assertEquals('michael', $this->request->getUsername());
59589 $this->assertEquals('', $this->request->getPassword());
59590
59591 // Remove the auth
59592 $this->request->setAuth(false);
59593 $this->assertEquals(null, $this->request->getUsername());
59594 $this->assertEquals(null, $this->request->getPassword());
59595
59596 // Make sure that the cURL based auth works too
59597 $request = new Request('GET', $this->getServer()->getUrl());
59598 $request->setAuth('michael', 'password', CURLAUTH_DIGEST);
59599 $this->assertEquals('michael:password', $request->getCurlOptions()->get(CURLOPT_USERPWD));
59600 $this->assertEquals(CURLAUTH_DIGEST, $request->getCurlOptions()->get(CURLOPT_HTTPAUTH));
59601 }
59602
59603 /**
59604 * @expectedException \Guzzle\Common\Exception\InvalidArgumentException
59605 */
59606 public function testValidatesAuth()
59607 {
59608 $this->request->setAuth('foo', 'bar', 'bam');
59609 }
59610
59611 public function testGetResourceUri()
59612 {
59613 $this->assertEquals('/', $this->request->getResource());
59614 $this->request->setPath('/index.html');
59615 $this->assertEquals('/index.html', $this->request->getResource());
59616 $this->request->getQuery()->add('v', '1');
59617 $this->assertEquals('/index.html?v=1', $this->request->getResource());
59618 }
59619
59620 public function testRequestHasMutableUrl()
59621 {
59622 $url = 'http://www.test.com:8081/path?q=123#fragment';
59623 $u = Url::factory($url);
59624 $this->assertSame($this->request, $this->request->setUrl($url));
59625 $this->assertEquals($url, $this->request->getUrl());
59626
59627 $this->assertSame($this->request, $this->request->setUrl($u));
59628 $this->assertEquals($url, $this->request->getUrl());
59629 }
59630
59631 public function testRequestHasState()
59632 {
59633 $this->assertEquals(RequestInterface::STATE_NEW, $this->request->getState());
59634 $this->request->setState(RequestInterface::STATE_TRANSFER);
59635 $this->assertEquals(RequestInterface::STATE_TRANSFER, $this->request->getState());
59636 }
59637
59638 public function testSetManualResponse()
59639 {
59640 $response = new Response(200, array(
59641 'Date' => 'Sat, 16 Oct 2010 17:27:14 GMT',
59642 'Expires' => '-1',
59643 'Cache-Control' => 'private, max-age=0',
59644 'Content-Type' => 'text/html; charset=ISO-8859-1',
59645 ), 'response body');
59646
59647 $this->assertSame($this->request, $this->request->setResponse($response), '-> setResponse() must use a fluent interface');
59648 $this->assertEquals('complete', $this->request->getState(), '-> setResponse() must change the state of the request to complete');
59649 $this->assertSame($response, $this->request->getResponse(), '-> setResponse() must set the exact same response that was passed in to it');
59650 }
59651
59652 public function testRequestCanHaveManuallySetResponseBody()
59653 {
59654 $file = __DIR__ . '/../../TestData/temp.out';
59655 if (file_exists($file)) {
59656 unlink($file);
59657 }
59658
59659 $this->getServer()->enqueue("HTTP/1.1 200 OK\r\nContent-Length: 4\r\n\r\ndata");
59660 $request = RequestFactory::getInstance()->create('GET', $this->getServer()->getUrl());
59661 $request->setClient($this->client);
59662 $entityBody = EntityBody::factory(fopen($file, 'w+'));
59663 $request->setResponseBody($entityBody);
59664 $response = $request->send();
59665 $this->assertSame($entityBody, $response->getBody());
59666
59667 $this->assertTrue(file_exists($file));
59668 $this->assertEquals('data', file_get_contents($file));
59669 unlink($file);
59670
59671 $this->assertEquals('data', $response->getBody(true));
59672 }
59673
59674 public function testHoldsCookies()
59675 {
59676 $this->assertNull($this->request->getCookie('test'));
59677
59678 // Set a cookie
59679 $this->assertSame($this->request, $this->request->addCookie('test', 'abc'));
59680 $this->assertEquals('abc', $this->request->getCookie('test'));
59681
59682 // Multiple cookies by setting the Cookie header
59683 $this->request->setHeader('Cookie', '__utma=1.638370270.1344367610.1374365610.1944450276.2; __utmz=1.1346368610.1.1.utmcsr=(direct)|utmccn=(direct)|utmcmd=(none); hl=de; PHPSESSID=ak93pqashi5uubuoq8fjv60897');
59684 $this->assertEquals('1.638370270.1344367610.1374365610.1944450276.2', $this->request->getCookie('__utma'));
59685 $this->assertEquals('1.1346368610.1.1.utmcsr=(direct)|utmccn=(direct)|utmcmd=(none)', $this->request->getCookie('__utmz'));
59686 $this->assertEquals('de', $this->request->getCookie('hl'));
59687 $this->assertEquals('ak93pqashi5uubuoq8fjv60897', $this->request->getCookie('PHPSESSID'));
59688
59689 // Unset the cookies by setting the Cookie header to null
59690 $this->request->setHeader('Cookie', null);
59691 $this->assertNull($this->request->getCookie('test'));
59692 $this->request->removeHeader('Cookie');
59693
59694 // Set and remove a cookie
59695 $this->assertSame($this->request, $this->request->addCookie('test', 'abc'));
59696 $this->assertEquals('abc', $this->request->getCookie('test'));
59697 $this->assertSame($this->request, $this->request->removeCookie('test'));
59698 $this->assertNull($this->request->getCookie('test'));
59699
59700 // Remove the cookie header
59701 $this->assertSame($this->request, $this->request->addCookie('test', 'abc'));
59702 $this->request->removeHeader('Cookie');
59703 $this->assertEquals('', (string) $this->request->getHeader('Cookie'));
59704
59705 // Remove a cookie value
59706 $this->request->addCookie('foo', 'bar')->addCookie('baz', 'boo');
59707 $this->request->removeCookie('foo');
59708 $this->assertEquals(array(
59709 'baz' => 'boo'
59710 ), $this->request->getCookies());
59711
59712 $this->request->addCookie('foo', 'bar');
59713 $this->assertEquals('baz=boo; foo=bar', (string) $this->request->getHeader('Cookie'));
59714 }
59715
59716 /**
59717 * @expectedException \Guzzle\Http\Exception\RequestException
59718 * @expectedExceptionMessage Error completing request
59719 */
59720 public function testRequestThrowsExceptionWhenSetToCompleteWithNoResponse()
59721 {
59722 $this->request->setState(RequestInterface::STATE_COMPLETE);
59723 }
59724
59725 public function testClonedRequestsUseNewInternalState()
59726 {
59727 $p = new AsyncPlugin();
59728 $this->request->getEventDispatcher()->addSubscriber($p);
59729 $h = $this->request->getHeader('Host');
59730
59731 $r = clone $this->request;
59732 $this->assertEquals(RequestInterface::STATE_NEW, $r->getState());
59733 $this->assertNotSame($r->getQuery(), $this->request->getQuery());
59734 $this->assertNotSame($r->getCurlOptions(), $this->request->getCurlOptions());
59735 $this->assertNotSame($r->getEventDispatcher(), $this->request->getEventDispatcher());
59736 $this->assertEquals($r->getHeaders(), $this->request->getHeaders());
59737 $this->assertNotSame($h, $r->getHeader('Host'));
59738 $this->assertNotSame($r->getParams(), $this->request->getParams());
59739 $this->assertTrue($this->request->getEventDispatcher()->hasListeners('request.sent'));
59740 }
59741
59742 public function testRecognizesBasicAuthCredentialsInUrls()
59743 {
59744 $this->request->setUrl('http://michael:test@test.com/');
59745 $this->assertEquals('michael', $this->request->getUsername());
59746 $this->assertEquals('test', $this->request->getPassword());
59747 }
59748
59749 public function testRequestCanBeSentUsingCurl()
59750 {
59751 $this->getServer()->flush();
59752 $this->getServer()->enqueue(array(
59753 "HTTP/1.1 200 OK\r\nContent-Length: 4\r\nExpires: Thu, 01 Dec 1994 16:00:00 GMT\r\nConnection: close\r\n\r\ndata",
59754 "HTTP/1.1 200 OK\r\nContent-Length: 4\r\nExpires: Thu, 01 Dec 1994 16:00:00 GMT\r\nConnection: close\r\n\r\ndata",
59755 "HTTP/1.1 404 Not Found\r\nContent-Encoding: application/xml\r\nContent-Length: 48\r\n\r\n<error><mesage>File not found</message></error>"
59756 ));
59757
59758 $request = RequestFactory::getInstance()->create('GET', $this->getServer()->getUrl());
59759 $request->setClient($this->client);
59760 $response = $request->send();
59761
59762 $this->assertEquals('data', $response->getBody(true));
59763 $this->assertEquals(200, (int) $response->getStatusCode());
59764 $this->assertEquals('OK', $response->getReasonPhrase());
59765 $this->assertEquals(4, $response->getContentLength());
59766 $this->assertEquals('Thu, 01 Dec 1994 16:00:00 GMT', $response->getExpires());
59767
59768 // Test that the same handle can be sent twice without setting state to new
59769 $response2 = $request->send();
59770 $this->assertNotSame($response, $response2);
59771
59772 try {
59773 $request = RequestFactory::getInstance()->create('GET', $this->getServer()->getUrl() . 'index.html');
59774 $request->setClient($this->client);
59775 $response = $request->send();
59776 $this->fail('Request did not receive a 404 response');
59777 } catch (BadResponseException $e) {
59778 }
59779
59780 $requests = $this->getServer()->getReceivedRequests(true);
59781 $messages = $this->getServer()->getReceivedRequests(false);
59782 $port = $this->getServer()->getPort();
59783
59784 $userAgent = $this->client->getDefaultUserAgent();
59785
59786 $this->assertEquals('127.0.0.1:' . $port, $requests[0]->getHeader('Host'));
59787 $this->assertEquals('127.0.0.1:' . $port, $requests[1]->getHeader('Host'));
59788 $this->assertEquals('127.0.0.1:' . $port, $requests[2]->getHeader('Host'));
59789
59790 $this->assertEquals('/', $requests[0]->getPath());
59791 $this->assertEquals('/', $requests[1]->getPath());
59792 $this->assertEquals('/index.html', $requests[2]->getPath());
59793
59794 $parts = explode("\r\n", $messages[0]);
59795 $this->assertEquals('GET / HTTP/1.1', $parts[0]);
59796
59797 $parts = explode("\r\n", $messages[1]);
59798 $this->assertEquals('GET / HTTP/1.1', $parts[0]);
59799
59800 $parts = explode("\r\n", $messages[2]);
59801 $this->assertEquals('GET /index.html HTTP/1.1', $parts[0]);
59802 }
59803
59804 public function testThrowsExceptionsWhenUnsuccessfulResponseIsReceivedByDefault()
59805 {
59806 $this->getServer()->flush();
59807 $this->getServer()->enqueue("HTTP/1.1 404 Not found\r\nContent-Length: 0\r\n\r\n");
59808
59809 try {
59810 $request = $this->client->get('/index.html');
59811 $response = $request->send();
59812 $this->fail('Request did not receive a 404 response');
59813 } catch (BadResponseException $e) {
59814 $this->assertContains('Client error response', $e->getMessage());
59815 $this->assertContains('[status code] 404', $e->getMessage());
59816 $this->assertContains('[reason phrase] Not found', $e->getMessage());
59817 }
59818 }
59819
59820 public function testCanShortCircuitErrorHandling()
59821 {
59822 $request = $this->request;
59823 $response = new Response(404);
59824 $request->setResponse($response, true);
59825 $out = '';
59826 $that = $this;
59827 $request->getEventDispatcher()->addListener('request.error', function($event) use (&$out, $that) {
59828 $out .= $event['request'] . "\n" . $event['response'] . "\n";
59829 $event->stopPropagation();
59830 });
59831 $request->send();
59832 $this->assertContains((string) $request, $out);
59833 $this->assertContains((string) $request->getResponse(), $out);
59834 $this->assertSame($response, $request->getResponse());
59835 }
59836
59837 public function testCanOverrideUnsuccessfulResponses()
59838 {
59839 $this->getServer()->flush();
59840 $this->getServer()->enqueue(array(
59841 "HTTP/1.1 404 NOT FOUND\r\n" .
59842 "Content-Length: 0\r\n" .
59843 "\r\n",
59844 "HTTP/1.1 200 OK\r\n" .
59845 "Content-Length: 0\r\n" .
59846 "\r\n"
59847 ));
59848
59849 $newResponse = null;
59850
59851 $request = $this->request;
59852 $request->getEventDispatcher()->addListener('request.error', function($event) use (&$newResponse) {
59853 if ($event['response']->getStatusCode() == 404) {
59854 $newRequest = clone $event['request'];
59855 $newResponse = $newRequest->send();
59856 // Override the original response and bypass additional response processing
59857 $event['response'] = $newResponse;
59858 // Call $event['request']->setResponse($newResponse); to re-apply events
59859 $event->stopPropagation();
59860 }
59861 });
59862
59863 $request->send();
59864
59865 $this->assertEquals(200, $request->getResponse()->getStatusCode());
59866 $this->assertSame($newResponse, $request->getResponse());
59867 $this->assertEquals(2, count($this->getServer()->getReceivedRequests()));
59868 }
59869
59870 public function testCanRetrieveUrlObject()
59871 {
59872 $request = new Request('GET', 'http://www.example.com/foo?abc=d');
59873 $this->assertInstanceOf('Guzzle\Http\Url', $request->getUrl(true));
59874 $this->assertEquals('http://www.example.com/foo?abc=d', $request->getUrl());
59875 $this->assertEquals('http://www.example.com/foo?abc=d', (string) $request->getUrl(true));
59876 }
59877
59878 public function testUnresolvedRedirectsReturnResponse()
59879 {
59880 $this->getServer()->flush();
59881 $this->getServer()->enqueue(array(
59882 "HTTP/1.1 303 SEE OTHER\r\nContent-Length: 0\r\n\r\n",
59883 "HTTP/1.1 301 Foo\r\nLocation: /foo\r\nContent-Length: 0\r\n\r\n"
59884 ));
59885 $request = $this->request;
59886 $this->assertEquals(303, $request->send()->getStatusCode());
59887 $request->getParams()->set(RedirectPlugin::DISABLE, true);
59888 $this->assertEquals(301, $request->send()->getStatusCode());
59889 }
59890
59891 public function testCanSendCustomRequests()
59892 {
59893 $this->getServer()->flush();
59894 $this->getServer()->enqueue("HTTP/1.1 200 OK\r\nContent-Length: 0\r\n\r\n");
59895 $request = $this->client->createRequest('PROPFIND', $this->getServer()->getUrl(), array(
59896 'Content-Type' => 'text/plain'
59897 ), 'foo');
59898 $response = $request->send();
59899 $requests = $this->getServer()->getReceivedRequests(true);
59900 $this->assertEquals('PROPFIND', $requests[0]->getMethod());
59901 $this->assertEquals(3, (string) $requests[0]->getHeader('Content-Length'));
59902 $this->assertEquals('foo', (string) $requests[0]->getBody());
59903 }
59904
59905 /**
59906 * @expectedException \PHPUnit_Framework_Error_Warning
59907 */
59908 public function testEnsuresFileCanBeCreated()
59909 {
59910 $this->getServer()->flush();
59911 $this->getServer()->enqueue("HTTP/1.1 200 OK\r\nContent-Length: 4\r\n\r\ntest");
59912 $this->client->get('/')->setResponseBody('/wefwefefefefwewefwe/wefwefwefefwe/wefwefewfw.txt')->send();
59913 }
59914
59915 public function testAllowsFilenameForDownloadingContent()
59916 {
59917 $this->getServer()->flush();
59918 $this->getServer()->enqueue("HTTP/1.1 200 OK\r\nContent-Length: 4\r\n\r\ntest");
59919 $name = sys_get_temp_dir() . '/foo.txt';
59920 $this->client->get('/')->setResponseBody($name)->send();
59921 $this->assertEquals('test', file_get_contents($name));
59922 unlink($name);
59923 }
59924
59925 public function testUsesCustomResponseBodyWhenItIsCustom()
59926 {
59927 $en = EntityBody::factory();
59928 $request = $this->client->get();
59929 $request->setResponseBody($en);
59930 $request->setResponse(new Response(200, array(), 'foo'));
59931 $this->assertEquals('foo', (string) $en);
59932 }
59933 }
59934 <?php
59935
59936 namespace Guzzle\Tests\Message;
59937
59938 use Guzzle\Common\Collection;
59939 use Guzzle\Http\ClientInterface;
59940 use Guzzle\Http\EntityBody;
59941 use Guzzle\Http\HttpException;
59942 use Guzzle\Http\Exception\BadResponseException;
59943 use Guzzle\Http\Message\Response;
59944
59945 /**
59946 * @group server
59947 * @covers Guzzle\Http\Message\Response
59948 */
59949 class ResponseTest extends \Guzzle\Tests\GuzzleTestCase
59950 {
59951 /** @var Response The response object to test */
59952 protected $response;
59953
59954 public function setup()
59955 {
59956 $this->response = new Response(200, new Collection(array(
59957 'Accept-Ranges' => 'bytes',
59958 'Age' => '12',
59959 'Allow' => 'GET, HEAD',
59960 'Cache-Control' => 'no-cache',
59961 'Content-Encoding' => 'gzip',
59962 'Content-Language' => 'da',
59963 'Content-Length' => '348',
59964 'Content-Location' => '/index.htm',
59965 'Content-Disposition' => 'attachment; filename=fname.ext',
59966 'Content-MD5' => 'Q2hlY2sgSW50ZWdyaXR5IQ==',
59967 'Content-Range' => 'bytes 21010-47021/47022',
59968 'Content-Type' => 'text/html; charset=utf-8',
59969 'Date' => 'Tue, 15 Nov 1994 08:12:31 GMT',
59970 'ETag' => '737060cd8c284d8af7ad3082f209582d',
59971 'Expires' => 'Thu, 01 Dec 1994 16:00:00 GMT',
59972 'Last-Modified' => 'Tue, 15 Nov 1994 12:45:26 GMT',
59973 'Location' => 'http://www.w3.org/pub/WWW/People.html',
59974 'Pragma' => 'no-cache',
59975 'Proxy-Authenticate' => 'Basic',
59976 'Retry-After' => '120',
59977 'Server' => 'Apache/1.3.27 (Unix) (Red-Hat/Linux)',
59978 'Set-Cookie' => 'UserID=JohnDoe; Max-Age=3600; Version=1',
59979 'Trailer' => 'Max-Forwards',
59980 'Transfer-Encoding' => 'chunked',
59981 'Vary' => '*',
59982 'Via' => '1.0 fred, 1.1 nowhere.com (Apache/1.1)',
59983 'Warning' => '199 Miscellaneous warning',
59984 'WWW-Authenticate' => 'Basic'
59985 )), 'body');
59986 }
59987
59988 public function tearDown()
59989 {
59990 unset($this->response);
59991 }
59992
59993 public function testConstructor()
59994 {
59995 $params = new Collection();
59996 $body = EntityBody::factory('');
59997 $response = new Response(200, $params, $body);
59998 $this->assertEquals(200, $response->getStatusCode());
59999 $this->assertEquals($body, $response->getBody());
60000 $this->assertEquals('OK', $response->getReasonPhrase());
60001 $this->assertEquals("HTTP/1.1 200 OK\r\n\r\n", $response->getRawHeaders());
60002
60003 // Make sure Content-Length is set automatically
60004 $response = new Response(200, $params);
60005 $this->assertEquals("HTTP/1.1 200 OK\r\n\r\n", $response->getRawHeaders());
60006
60007 // Pass bodies to the response
60008 $response = new Response(200, null, 'data');
60009 $this->assertInstanceOf('Guzzle\\Http\\EntityBody', $response->getBody());
60010 $response = new Response(200, null, EntityBody::factory('data'));
60011 $this->assertInstanceOf('Guzzle\\Http\\EntityBody', $response->getBody());
60012 $this->assertEquals('data', $response->getBody(true));
60013 $response = new Response(200, null, '0');
60014 $this->assertSame('0', $response->getBody(true), 'getBody(true) should return "0" if response body is "0".');
60015
60016 // Make sure the proper exception is thrown
60017 try {
60018 //$response = new Response(200, null, array('foo' => 'bar'));
60019 //$this->fail('Response did not throw exception when passing invalid body');
60020 } catch (HttpException $e) {
60021 }
60022
60023 // Ensure custom codes can be set
60024 $response = new Response(2);
60025 $this->assertEquals(2, $response->getStatusCode());
60026 $this->assertEquals('', $response->getReasonPhrase());
60027
60028 // Make sure the proper exception is thrown when sending invalid headers
60029 try {
60030 $response = new Response(200, 'adidas');
60031 $this->fail('Response did not throw exception when passing invalid $headers');
60032 } catch (BadResponseException $e) {
60033 }
60034 }
60035
60036 public function test__toString()
60037 {
60038 $response = new Response(200);
60039 $this->assertEquals("HTTP/1.1 200 OK\r\n\r\n", (string) $response);
60040
60041 // Add another header
60042 $response = new Response(200, array(
60043 'X-Test' => 'Guzzle'
60044 ));
60045 $this->assertEquals("HTTP/1.1 200 OK\r\nX-Test: Guzzle\r\n\r\n", (string) $response);
60046
60047 $response = new Response(200, array(
60048 'Content-Length' => 4
60049 ), 'test');
60050 $this->assertEquals("HTTP/1.1 200 OK\r\nContent-Length: 4\r\n\r\ntest", (string) $response);
60051 }
60052
60053 public function testFactory()
60054 {
60055 $response = Response::fromMessage("HTTP/1.1 200 OK\r\nContent-Length: 4\r\n\r\ntest");
60056 $this->assertEquals(200, $response->getStatusCode());
60057 $this->assertEquals('OK', $response->getReasonPhrase());
60058 $this->assertEquals(4, (string) $response->getContentLength());
60059 $this->assertEquals('test', $response->getBody(true));
60060
60061 // Make sure that automatic Content-Length works
60062 $response = Response::fromMessage("HTTP/1.1 200 OK\r\nContent-Length: 4\r\n\r\ntest");
60063 $this->assertEquals(4, (string) $response->getContentLength());
60064 $this->assertEquals('test', $response->getBody(true));
60065 }
60066
60067 public function testFactoryCanCreateHeadResponses()
60068 {
60069 $response = Response::fromMessage("HTTP/1.1 200 OK\r\nContent-Length: 4\r\n\r\n");
60070 $this->assertEquals(200, $response->getStatusCode());
60071 $this->assertEquals('OK', $response->getReasonPhrase());
60072 $this->assertEquals(4, (string) $response->getContentLength());
60073 $this->assertEquals('', $response->getBody(true));
60074 }
60075
60076 public function testFactoryRequiresMessage()
60077 {
60078 $this->assertFalse(Response::fromMessage(''));
60079 }
60080
60081 public function testGetBody()
60082 {
60083 $body = EntityBody::factory('');
60084 $response = new Response(403, new Collection(), $body);
60085 $this->assertEquals($body, $response->getBody());
60086 $response->setBody('foo');
60087 $this->assertEquals('foo', $response->getBody(true));
60088 }
60089
60090 public function testManagesStatusCode()
60091 {
60092 $response = new Response(403);
60093 $this->assertEquals(403, $response->getStatusCode());
60094 }
60095
60096 public function testGetMessage()
60097 {
60098 $response = new Response(200, new Collection(array(
60099 'Content-Length' => 4
60100 )), 'body');
60101
60102 $this->assertEquals("HTTP/1.1 200 OK\r\nContent-Length: 4\r\n\r\nbody", $response->getMessage());
60103 }
60104
60105 public function testGetRawHeaders()
60106 {
60107 $response = new Response(200, new Collection(array(
60108 'Keep-Alive' => 155,
60109 'User-Agent' => 'Guzzle',
60110 'Content-Length' => 4
60111 )), 'body');
60112
60113 $this->assertEquals("HTTP/1.1 200 OK\r\nKeep-Alive: 155\r\nUser-Agent: Guzzle\r\nContent-Length: 4\r\n\r\n", $response->getRawHeaders());
60114 }
60115
60116 public function testHandlesStatusAndStatusCodes()
60117 {
60118 $response = new Response(200, new Collection(), 'body');
60119 $this->assertEquals('OK', $response->getReasonPhrase());
60120
60121 $this->assertSame($response, $response->setStatus(204));
60122 $this->assertEquals('No Content', $response->getReasonPhrase());
60123 $this->assertEquals(204, $response->getStatusCode());
60124
60125 $this->assertSame($response, $response->setStatus(204, 'Testing!'));
60126 $this->assertEquals('Testing!', $response->getReasonPhrase());
60127 $this->assertEquals(204, $response->getStatusCode());
60128
60129 $response->setStatus(2000);
60130 $this->assertEquals(2000, $response->getStatusCode());
60131 $this->assertEquals('', $response->getReasonPhrase());
60132
60133 $response->setStatus(200, 'Foo');
60134 $this->assertEquals(200, $response->getStatusCode());
60135 $this->assertEquals('Foo', $response->getReasonPhrase());
60136 }
60137
60138 public function testIsClientError()
60139 {
60140 $response = new Response(403);
60141 $this->assertTrue($response->isClientError());
60142 $response = new Response(200);
60143 $this->assertFalse($response->isClientError());
60144 }
60145
60146 public function testIsError()
60147 {
60148 $response = new Response(403);
60149 $this->assertTrue($response->isError());
60150 $response = new Response(200);
60151 $this->assertFalse($response->isError());
60152 $response = new Response(500);
60153 $this->assertTrue($response->isError());
60154 }
60155
60156 public function testIsInformational()
60157 {
60158 $response = new Response(100);
60159 $this->assertTrue($response->isInformational());
60160 $response = new Response(200);
60161 $this->assertFalse($response->isInformational());
60162 }
60163
60164 public function testIsRedirect()
60165 {
60166 $response = new Response(301);
60167 $this->assertTrue($response->isRedirect());
60168 $response = new Response(200);
60169 $this->assertFalse($response->isRedirect());
60170 }
60171
60172 public function testIsServerError()
60173 {
60174 $response = new Response(500);
60175 $this->assertTrue($response->isServerError());
60176 $response = new Response(400);
60177 $this->assertFalse($response->isServerError());
60178 }
60179
60180 public function testIsSuccessful()
60181 {
60182 $response = new Response(200);
60183 $this->assertTrue($response->isSuccessful());
60184 $response = new Response(403);
60185 $this->assertFalse($response->isSuccessful());
60186 }
60187
60188 public function testGetAcceptRanges()
60189 {
60190 $this->assertEquals('bytes', $this->response->getAcceptRanges());
60191 }
60192
60193 public function testCalculatesAge()
60194 {
60195 $this->assertEquals(12, $this->response->calculateAge());
60196
60197 $this->response->removeHeader('Age');
60198 $this->response->removeHeader('Date');
60199 $this->assertNull($this->response->calculateAge());
60200
60201 $this->response->setHeader('Date', gmdate(ClientInterface::HTTP_DATE, strtotime('-1 minute')));
60202 // If the test runs slowly, still pass with a +5 second allowance
60203 $this->assertTrue($this->response->getAge() - 60 <= 5);
60204 }
60205
60206 public function testGetAllow()
60207 {
60208 $this->assertEquals('GET, HEAD', $this->response->getAllow());
60209 }
60210
60211 public function testGetCacheControl()
60212 {
60213 $this->assertEquals('no-cache', $this->response->getCacheControl());
60214 }
60215
60216 public function testGetContentEncoding()
60217 {
60218 $this->assertEquals('gzip', $this->response->getContentEncoding());
60219 }
60220
60221 public function testGetContentLanguage()
60222 {
60223 $this->assertEquals('da', $this->response->getContentLanguage());
60224 }
60225
60226 public function testGetContentLength()
60227 {
60228 $this->assertEquals('348', $this->response->getContentLength());
60229 }
60230
60231 public function testGetContentLocation()
60232 {
60233 $this->assertEquals('/index.htm', $this->response->getContentLocation());
60234 }
60235
60236 public function testGetContentDisposition()
60237 {
60238 $this->assertEquals('attachment; filename=fname.ext', $this->response->getContentDisposition());
60239 }
60240
60241 public function testGetContentMd5()
60242 {
60243 $this->assertEquals('Q2hlY2sgSW50ZWdyaXR5IQ==', $this->response->getContentMd5());
60244 }
60245
60246 public function testGetContentRange()
60247 {
60248 $this->assertEquals('bytes 21010-47021/47022', $this->response->getContentRange());
60249 }
60250
60251 public function testGetContentType()
60252 {
60253 $this->assertEquals('text/html; charset=utf-8', $this->response->getContentType());
60254 }
60255
60256 public function testGetDate()
60257 {
60258 $this->assertEquals('Tue, 15 Nov 1994 08:12:31 GMT', $this->response->getDate());
60259 }
60260
60261 public function testGetEtag()
60262 {
60263 $this->assertEquals('737060cd8c284d8af7ad3082f209582d', $this->response->getEtag());
60264 }
60265
60266 public function testGetExpires()
60267 {
60268 $this->assertEquals('Thu, 01 Dec 1994 16:00:00 GMT', $this->response->getExpires());
60269 }
60270
60271 public function testGetLastModified()
60272 {
60273 $this->assertEquals('Tue, 15 Nov 1994 12:45:26 GMT', $this->response->getLastModified());
60274 }
60275
60276 public function testGetLocation()
60277 {
60278 $this->assertEquals('http://www.w3.org/pub/WWW/People.html', $this->response->getLocation());
60279 }
60280
60281 public function testGetPragma()
60282 {
60283 $this->assertEquals('no-cache', $this->response->getPragma());
60284 }
60285
60286 public function testGetProxyAuthenticate()
60287 {
60288 $this->assertEquals('Basic', $this->response->getProxyAuthenticate());
60289 }
60290
60291 public function testGetServer()
60292 {
60293 $this->assertEquals('Apache/1.3.27 (Unix) (Red-Hat/Linux)', $this->response->getServer());
60294 }
60295
60296 public function testGetSetCookie()
60297 {
60298 $this->assertEquals('UserID=JohnDoe; Max-Age=3600; Version=1', $this->response->getSetCookie());
60299 }
60300
60301 public function testGetMultipleSetCookie()
60302 {
60303 $this->response->addHeader('Set-Cookie', 'UserID=Mike; Max-Age=200');
60304 $this->assertEquals(array(
60305 'UserID=JohnDoe; Max-Age=3600; Version=1',
60306 'UserID=Mike; Max-Age=200',
60307 ), $this->response->getHeader('Set-Cookie')->toArray());
60308 }
60309
60310 public function testGetSetCookieNormalizesHeaders()
60311 {
60312 $this->response->addHeaders(array(
60313 'Set-Cooke' => 'boo',
60314 'set-cookie' => 'foo'
60315 ));
60316
60317 $this->assertEquals(array(
60318 'UserID=JohnDoe; Max-Age=3600; Version=1',
60319 'foo'
60320 ), $this->response->getHeader('Set-Cookie')->toArray());
60321
60322 $this->response->addHeaders(array(
60323 'set-cookie' => 'fubu'
60324 ));
60325 $this->assertEquals(
60326 array('UserID=JohnDoe; Max-Age=3600; Version=1', 'foo', 'fubu'),
60327 $this->response->getHeader('Set-Cookie')->toArray()
60328 );
60329 }
60330
60331 public function testGetTrailer()
60332 {
60333 $this->assertEquals('Max-Forwards', $this->response->getTrailer());
60334 }
60335
60336 public function testGetTransferEncoding()
60337 {
60338 $this->assertEquals('chunked', $this->response->getTransferEncoding());
60339 }
60340
60341 public function testGetVary()
60342 {
60343 $this->assertEquals('*', $this->response->getVary());
60344 }
60345
60346 public function testReturnsViaHeader()
60347 {
60348 $this->assertEquals('1.0 fred, 1.1 nowhere.com (Apache/1.1)', $this->response->getVia());
60349 }
60350 public function testGetWarning()
60351 {
60352 $this->assertEquals('199 Miscellaneous warning', $this->response->getWarning());
60353 }
60354
60355 public function testReturnsWwwAuthenticateHeader()
60356 {
60357 $this->assertEquals('Basic', $this->response->getWwwAuthenticate());
60358 }
60359
60360 public function testReturnsConnectionHeader()
60361 {
60362 $this->assertEquals(null, $this->response->getConnection());
60363 $this->response->setHeader('Connection', 'close');
60364 $this->assertEquals('close', $this->response->getConnection());
60365 }
60366
60367 public function testReturnsHeaders()
60368 {
60369 $this->assertEquals('Basic', $this->response->getHeader('WWW-Authenticate', null, true));
60370 $this->assertEquals('chunked', $this->response->getHeader('Transfer-Encoding', null, false));
60371 }
60372
60373 public function testHasTransferInfo()
60374 {
60375 $stats = array (
60376 'url' => 'http://www.google.com/',
60377 'content_type' => 'text/html; charset=ISO-8859-1',
60378 'http_code' => 200,
60379 'header_size' => 606,
60380 'request_size' => 53,
60381 'filetime' => -1,
60382 'ssl_verify_result' => 0,
60383 'redirect_count' => 0,
60384 'total_time' => 0.093284,
60385 'namelookup_time' => 0.001349,
60386 'connect_time' => 0.01635,
60387 'pretransfer_time' => 0.016358,
60388 'size_upload' => 0,
60389 'size_download' => 10330,
60390 'speed_download' => 110737,
60391 'speed_upload' => 0,
60392 'download_content_length' => -1,
60393 'upload_content_length' => 0,
60394 'starttransfer_time' => 0.07066,
60395 'redirect_time' => 0,
60396 );
60397
60398 // Uninitialized state
60399 $this->assertNull($this->response->getInfo('url'));
60400 $this->assertEquals(array(), $this->response->getInfo());
60401
60402 // Set the stats
60403 $this->response->setInfo($stats);
60404 $this->assertEquals($stats, $this->response->getInfo());
60405 $this->assertEquals(606, $this->response->getInfo('header_size'));
60406 $this->assertNull($this->response->getInfo('does_not_exist'));
60407 }
60408
60409 /**
60410 * @return Response
60411 */
60412 private function getResponse($code, array $headers = null, EntityBody $body = null)
60413 {
60414 return new Response($code, $headers, $body);
60415 }
60416
60417 public function testDeterminesIfItCanBeCached()
60418 {
60419 $this->assertTrue($this->getResponse(200)->canCache());
60420 $this->assertTrue($this->getResponse(410)->canCache());
60421 $this->assertFalse($this->getResponse(404)->canCache());
60422 $this->assertTrue($this->getResponse(200, array(
60423 'Cache-Control' => 'public'
60424 ))->canCache());
60425
60426 // This has the no-store directive
60427 $this->assertFalse($this->getResponse(200, array(
60428 'Cache-Control' => 'private, no-store'
60429 ))->canCache());
60430
60431 // The body cannot be read, so it cannot be cached
60432 $tmp = tempnam('/tmp', 'not-readable');
60433 $resource = fopen($tmp, 'w');
60434 $this->assertFalse($this->getResponse(200, array(
60435 'Transfer-Encoding' => 'chunked'
60436 ), EntityBody::factory($resource, 10))->canCache());
60437 unlink($tmp);
60438
60439 // The body is 0 length, cannot be read, so it can be cached
60440 $tmp = tempnam('/tmp', 'not-readable');
60441 $resource = fopen($tmp, 'w');
60442 $this->assertTrue($this->getResponse(200, array(array(
60443 'Content-Length' => 0
60444 )), EntityBody::factory($resource, 0))->canCache());
60445 unlink($tmp);
60446 }
60447
60448 public function testDeterminesResponseMaxAge()
60449 {
60450 $this->assertEquals(null, $this->getResponse(200)->getMaxAge());
60451
60452 // Uses the response's s-maxage
60453 $this->assertEquals(140, $this->getResponse(200, array(
60454 'Cache-Control' => 's-maxage=140'
60455 ))->getMaxAge());
60456
60457 // Uses the response's max-age
60458 $this->assertEquals(120, $this->getResponse(200, array(
60459 'Cache-Control' => 'max-age=120'
60460 ))->getMaxAge());
60461
60462 // Uses the response's max-age
60463 $this->assertEquals(120, $this->getResponse(200, array(
60464 'Cache-Control' => 'max-age=120',
60465 'Expires' => gmdate(ClientInterface::HTTP_DATE, strtotime('+1 day'))
60466 ))->getMaxAge());
60467
60468 // Uses the Expires date
60469 $this->assertGreaterThanOrEqual(82400, $this->getResponse(200, array(
60470 'Expires' => gmdate(ClientInterface::HTTP_DATE, strtotime('+1 day'))
60471 ))->getMaxAge());
60472
60473 // Uses the Expires date
60474 $this->assertGreaterThanOrEqual(82400, $this->getResponse(200, array(
60475 'Expires' => gmdate(ClientInterface::HTTP_DATE, strtotime('+1 day'))
60476 ))->getMaxAge());
60477 }
60478
60479 public function testDeterminesIfItCanValidate()
60480 {
60481 $response = new Response(200);
60482 $this->assertFalse($response->canValidate());
60483 $response->setHeader('ETag', '123');
60484 $this->assertTrue($response->canValidate());
60485 $response->removeHeader('ETag');
60486 $this->assertFalse($response->canValidate());
60487 $response->setHeader('Last-Modified', '123');
60488 $this->assertTrue($response->canValidate());
60489 }
60490
60491 public function testCalculatesFreshness()
60492 {
60493 $response = new Response(200);
60494 $this->assertNull($response->isFresh());
60495 $this->assertNull($response->getFreshness());
60496
60497 $response->setHeader('Cache-Control', 'max-age=120');
60498 $response->setHeader('Age', 100);
60499 $this->assertEquals(20, $response->getFreshness());
60500 $this->assertTrue($response->isFresh());
60501
60502 $response->setHeader('Age', 120);
60503 $this->assertEquals(0, $response->getFreshness());
60504 $this->assertTrue($response->isFresh());
60505
60506 $response->setHeader('Age', 150);
60507 $this->assertEquals(-30, $response->getFreshness());
60508 $this->assertFalse($response->isFresh());
60509 }
60510
60511 public function testHandlesProtocols()
60512 {
60513 $this->assertSame($this->response, $this->response->setProtocol('HTTP', '1.0'));
60514 $this->assertEquals('HTTP', $this->response->getProtocol());
60515 $this->assertEquals('1.0', $this->response->getProtocolVersion());
60516 }
60517
60518 public function testComparesContentType()
60519 {
60520 $response = new Response(200, array(
60521 'Content-Type' => 'text/html; charset=ISO-8859-4'
60522 ));
60523
60524 $this->assertTrue($response->isContentType('text/html'));
60525 $this->assertTrue($response->isContentType('TExT/html'));
60526 $this->assertTrue($response->isContentType('charset=ISO-8859-4'));
60527 $this->assertFalse($response->isContentType('application/xml'));
60528 }
60529
60530 public function testResponseDeterminesIfMethodIsAllowedBaseOnAllowHeader()
60531 {
60532 $response = new Response(200, array(
60533 'Allow' => 'OPTIONS, POST, deletE,GET'
60534 ));
60535
60536 $this->assertTrue($response->isMethodAllowed('get'));
60537 $this->assertTrue($response->isMethodAllowed('GET'));
60538 $this->assertTrue($response->isMethodAllowed('options'));
60539 $this->assertTrue($response->isMethodAllowed('post'));
60540 $this->assertTrue($response->isMethodAllowed('Delete'));
60541 $this->assertFalse($response->isMethodAllowed('put'));
60542 $this->assertFalse($response->isMethodAllowed('PUT'));
60543
60544 $response = new Response(200);
60545 $this->assertFalse($response->isMethodAllowed('get'));
60546 }
60547
60548 public function testParsesJsonResponses()
60549 {
60550 $response = new Response(200, array(), '{"foo": "bar"}');
60551 $this->assertEquals(array('foo' => 'bar'), $response->json());
60552 // Return array when null is a service response
60553 $response = new Response(200);
60554 $this->assertEquals(array(), $response->json());
60555 }
60556
60557 /**
60558 * @expectedException \Guzzle\Common\Exception\RuntimeException
60559 * @expectedExceptionMessage Unable to parse response body into JSON: 4
60560 */
60561 public function testThrowsExceptionWhenFailsToParseJsonResponse()
60562 {
60563 $response = new Response(200, array(), '{"foo": "');
60564 $response->json();
60565 }
60566
60567 public function testParsesXmlResponses()
60568 {
60569 $response = new Response(200, array(), '<abc><foo>bar</foo></abc>');
60570 $this->assertEquals('bar', (string) $response->xml()->foo);
60571 // Always return a SimpleXMLElement from the xml method
60572 $response = new Response(200);
60573 $this->assertEmpty((string) $response->xml()->foo);
60574 }
60575
60576 /**
60577 * @expectedException \Guzzle\Common\Exception\RuntimeException
60578 * @expectedExceptionMessage Unable to parse response body into XML: String could not be parsed as XML
60579 */
60580 public function testThrowsExceptionWhenFailsToParseXmlResponse()
60581 {
60582 $response = new Response(200, array(), '<abc');
60583 $response->xml();
60584 }
60585
60586 public function testResponseIsSerializable()
60587 {
60588 $response = new Response(200, array('Foo' => 'bar'), 'test');
60589 $r = unserialize(serialize($response));
60590 $this->assertEquals(200, $r->getStatusCode());
60591 $this->assertEquals('bar', (string) $r->getHeader('Foo'));
60592 $this->assertEquals('test', (string) $r->getBody());
60593 }
60594 }
60595 <?php
60596
60597 namespace Guzzle\Tests\Http;
60598
60599 use Guzzle\Http\QueryString;
60600 use Guzzle\Http\QueryAggregator\DuplicateAggregator;
60601 use Guzzle\Http\QueryAggregator\CommaAggregator;
60602
60603 class QueryStringTest extends \Guzzle\Tests\GuzzleTestCase
60604 {
60605 /** @var \Guzzle\Http\QueryString The query string object to test */
60606 protected $q;
60607
60608 public function setup()
60609 {
60610 $this->q = new QueryString();
60611 }
60612
60613 public function testGetFieldSeparator()
60614 {
60615 $this->assertEquals('&', $this->q->getFieldSeparator());
60616 }
60617
60618 public function testGetValueSeparator()
60619 {
60620 $this->assertEquals('=', $this->q->getValueSeparator());
60621 }
60622
60623 public function testIsUrlEncoding()
60624 {
60625 $this->assertEquals('RFC 3986', $this->q->getUrlEncoding());
60626 $this->assertTrue($this->q->isUrlEncoding());
60627 $this->assertEquals('foo%20bar', $this->q->encodeValue('foo bar'));
60628
60629 $this->q->useUrlEncoding(QueryString::FORM_URLENCODED);
60630 $this->assertTrue($this->q->isUrlEncoding());
60631 $this->assertEquals(QueryString::FORM_URLENCODED, $this->q->getUrlEncoding());
60632 $this->assertEquals('foo+bar', $this->q->encodeValue('foo bar'));
60633
60634 $this->assertSame($this->q, $this->q->useUrlEncoding(false));
60635 $this->assertFalse($this->q->isUrlEncoding());
60636 $this->assertFalse($this->q->isUrlEncoding());
60637 }
60638
60639 public function testSetFieldSeparator()
60640 {
60641 $this->assertEquals($this->q, $this->q->setFieldSeparator('/'));
60642 $this->assertEquals('/', $this->q->getFieldSeparator());
60643 }
60644
60645 public function testSetValueSeparator()
60646 {
60647 $this->assertEquals($this->q, $this->q->setValueSeparator('/'));
60648 $this->assertEquals('/', $this->q->getValueSeparator());
60649 }
60650
60651 public function testUrlEncode()
60652 {
60653 $params = array(
60654 'test' => 'value',
60655 'test 2' => 'this is a test?',
60656 'test3' => array('v1', 'v2', 'v3'),
60657 'ሴ' => 'bar'
60658 );
60659 $encoded = array(
60660 'test' => 'value',
60661 'test%202' => rawurlencode('this is a test?'),
60662 'test3%5B0%5D' => 'v1',
60663 'test3%5B1%5D' => 'v2',
60664 'test3%5B2%5D' => 'v3',
60665 '%E1%88%B4' => 'bar'
60666 );
60667 $this->q->replace($params);
60668 $this->assertEquals($encoded, $this->q->urlEncode());
60669
60670 // Disable encoding
60671 $testData = array('test 2' => 'this is a test');
60672 $this->q->replace($testData);
60673 $this->q->useUrlEncoding(false);
60674 $this->assertEquals($testData, $this->q->urlEncode());
60675 }
60676
60677 public function testToString()
60678 {
60679 // Check with no parameters
60680 $this->assertEquals('', $this->q->__toString());
60681
60682 $params = array(
60683 'test' => 'value',
60684 'test 2' => 'this is a test?',
60685 'test3' => array('v1', 'v2', 'v3'),
60686 'test4' => null,
60687 );
60688 $this->q->replace($params);
60689 $this->assertEquals('test=value&test%202=this%20is%20a%20test%3F&test3%5B0%5D=v1&test3%5B1%5D=v2&test3%5B2%5D=v3&test4=', $this->q->__toString());
60690 $this->q->useUrlEncoding(false);
60691 $this->assertEquals('test=value&test 2=this is a test?&test3[0]=v1&test3[1]=v2&test3[2]=v3&test4=', $this->q->__toString());
60692
60693 // Use an alternative aggregator
60694 $this->q->setAggregator(new CommaAggregator());
60695 $this->assertEquals('test=value&test 2=this is a test?&test3=v1,v2,v3&test4=', $this->q->__toString());
60696 }
60697
60698 public function testAllowsMultipleValuesPerKey()
60699 {
60700 $q = new QueryString();
60701 $q->add('facet', 'size');
60702 $q->add('facet', 'width');
60703 $q->add('facet.field', 'foo');
60704 // Use the duplicate aggregator
60705 $q->setAggregator(new DuplicateAggregator());
60706 $this->assertEquals('facet=size&facet=width&facet.field=foo', $q->__toString());
60707 }
60708
60709 public function testAllowsNestedQueryData()
60710 {
60711 $this->q->replace(array(
60712 'test' => 'value',
60713 't' => array(
60714 'v1' => 'a',
60715 'v2' => 'b',
60716 'v3' => array(
60717 'v4' => 'c',
60718 'v5' => 'd',
60719 )
60720 )
60721 ));
60722
60723 $this->q->useUrlEncoding(false);
60724 $this->assertEquals('test=value&t[v1]=a&t[v2]=b&t[v3][v4]=c&t[v3][v5]=d', $this->q->__toString());
60725 }
60726
60727 public function parseQueryProvider()
60728 {
60729 return array(
60730 // Ensure that multiple query string values are allowed per value
60731 array('q=a&q=b', array('q' => array('a', 'b'))),
60732 // Ensure that PHP array style query string values are parsed
60733 array('q[]=a&q[]=b', array('q' => array('a', 'b'))),
60734 // Ensure that a single PHP array style query string value is parsed into an array
60735 array('q[]=a', array('q' => array('a'))),
60736 // Ensure that decimals are allowed in query strings
60737 array('q.a=a&q.b=b', array(
60738 'q.a' => 'a',
60739 'q.b' => 'b'
60740 )),
60741 // Ensure that query string values are percent decoded
60742 array('q%20a=a%20b', array('q a' => 'a b')),
60743 // Ensure null values can be added
60744 array('q&a', array('q' => null, 'a' => null)),
60745 );
60746 }
60747
60748 /**
60749 * @dataProvider parseQueryProvider
60750 */
60751 public function testParsesQueryStrings($query, $data)
60752 {
60753 $query = QueryString::fromString($query);
60754 $this->assertEquals($data, $query->getAll());
60755 }
60756
60757 public function testProperlyDealsWithDuplicateQueryStringValues()
60758 {
60759 $query = QueryString::fromString('foo=a&foo=b&?µ=c');
60760 $this->assertEquals(array('a', 'b'), $query->get('foo'));
60761 $this->assertEquals('c', $query->get('?µ'));
60762 }
60763
60764 public function testAllowsBlankQueryStringValues()
60765 {
60766 $query = QueryString::fromString('foo');
60767 $this->assertEquals('foo=', (string) $query);
60768 $query->set('foo', QueryString::BLANK);
60769 $this->assertEquals('foo', (string) $query);
60770 }
60771
60772 public function testAllowsFalsyQueryStringValues()
60773 {
60774 $query = QueryString::fromString('0');
60775 $this->assertEquals('0=', (string) $query);
60776 $query->set('0', QueryString::BLANK);
60777 $this->assertSame('0', (string) $query);
60778 }
60779
60780 public function testFromStringIgnoresQuestionMark()
60781 {
60782 $query = QueryString::fromString('foo=baz&bar=boo');
60783 $this->assertEquals('foo=baz&bar=boo', (string) $query);
60784 }
60785
60786 public function testConvertsPlusSymbolsToSpaces()
60787 {
60788 $query = QueryString::fromString('var=foo+bar');
60789 $this->assertEquals('foo bar', $query->get('var'));
60790 }
60791
60792 public function testFromStringDoesntMangleZeroes()
60793 {
60794 $query = QueryString::fromString('var=0');
60795 $this->assertSame('0', $query->get('var'));
60796 }
60797
60798 public function testAllowsZeroValues()
60799 {
60800 $query = new QueryString(array(
60801 'foo' => 0,
60802 'baz' => '0',
60803 'bar' => null,
60804 'boo' => false
60805 ));
60806 $this->assertEquals('foo=0&baz=0&bar=&boo=', (string) $query);
60807 }
60808
60809 public function testFromStringDoesntStripTrailingEquals()
60810 {
60811 $query = QueryString::fromString('data=mF0b3IiLCJUZWFtIERldiJdfX0=');
60812 $this->assertEquals('mF0b3IiLCJUZWFtIERldiJdfX0=', $query->get('data'));
60813 }
60814
60815 public function testGuessesIfDuplicateAggregatorShouldBeUsed()
60816 {
60817 $query = QueryString::fromString('test=a&test=b');
60818 $this->assertEquals('test=a&test=b', (string) $query);
60819 }
60820
60821 public function testGuessesIfDuplicateAggregatorShouldBeUsedAndChecksForPhpStyle()
60822 {
60823 $query = QueryString::fromString('test[]=a&test[]=b');
60824 $this->assertEquals('test%5B0%5D=a&test%5B1%5D=b', (string) $query);
60825 }
60826 }
60827 <?php
60828
60829 namespace Guzzle\Tests\Http\Exception;
60830
60831 use Guzzle\Http\Exception\CurlException;
60832 use Guzzle\Http\Curl\CurlHandle;
60833
60834 /**
60835 * @covers Guzzle\Http\Exception\CurlException
60836 */
60837 class CurlExceptionTest extends \Guzzle\Tests\GuzzleTestCase
60838 {
60839 public function testStoresCurlError()
60840 {
60841 $e = new CurlException();
60842 $this->assertNull($e->getError());
60843 $this->assertNull($e->getErrorNo());
60844 $this->assertSame($e, $e->setError('test', 12));
60845 $this->assertEquals('test', $e->getError());
60846 $this->assertEquals(12, $e->getErrorNo());
60847
60848 $handle = new CurlHandle(curl_init(), array());
60849 $e->setCurlHandle($handle);
60850 $this->assertSame($handle, $e->getCurlHandle());
60851 $handle->close();
60852 }
60853 }
60854 <?php
60855
60856 namespace Guzzle\Tests\Http\Exception;
60857
60858 use Guzzle\Http\Exception\MultiTransferException;
60859 use Guzzle\Http\Curl\CurlHandle;
60860 use Guzzle\Http\Message\Request;
60861
60862 /**
60863 * @covers Guzzle\Http\Exception\MultiTransferException
60864 */
60865 class MultiTransferExceptionTest extends \Guzzle\Tests\GuzzleTestCase
60866 {
60867 public function testHasRequests()
60868 {
60869 $r1 = new Request('GET', 'http://www.foo.com');
60870 $r2 = new Request('GET', 'http://www.foo.com');
60871 $e = new MultiTransferException();
60872 $e->addSuccessfulRequest($r1);
60873 $e->addFailedRequest($r2);
60874 $this->assertEquals(array($r1), $e->getSuccessfulRequests());
60875 $this->assertEquals(array($r2), $e->getSuccessfulRequests());
60876 $this->assertEquals(array($r1, $r2), $e->getAllRequests());
60877 $this->assertTrue($e->containsRequest($r1));
60878 $this->assertTrue($e->containsRequest($r2));
60879 $this->assertFalse($e->containsRequest(new Request('POST', '/foo')));
60880 }
60881
60882 public function testCanSetRequests()
60883 {
60884 $s = array($r1 = new Request('GET', 'http://www.foo.com'));
60885 $f = array($r2 = new Request('GET', 'http://www.foo.com'));
60886 $e = new MultiTransferException();
60887 $e->setSuccessfulRequests($s);
60888 $e->setFailedRequests($f);
60889 $this->assertEquals(array($r1), $e->getSuccessfulRequests());
60890 $this->assertEquals(array($r2), $e->getSuccessfulRequests());
60891 }
60892
60893 public function testAssociatesExceptionsWithRequests()
60894 {
60895 $r1 = new Request('GET', 'http://www.foo.com');
60896 $re1 = new \Exception('foo');
60897 $re2 = new \Exception('bar');
60898 $e = new MultiTransferException();
60899 $e->add($re2);
60900 $e->addFailedRequestWithException($r1, $re1);
60901 $this->assertSame($re1, $e->getExceptionForFailedRequest($r1));
60902 $this->assertNull($e->getExceptionForFailedRequest(new Request('POST', '/foo')));
60903 }
60904 }
60905 <?php
60906
60907 namespace Guzzle\Tests\Http\Exception;
60908
60909 use Guzzle\Http\Message\Response;
60910 use Guzzle\Http\Message\Request;
60911 use Guzzle\Http\Exception\RequestException;
60912 use Guzzle\Http\Exception\BadResponseException;
60913
60914 class ExceptionTest extends \Guzzle\Tests\GuzzleTestCase
60915 {
60916 /**
60917 * @covers Guzzle\Http\Exception\RequestException
60918 */
60919 public function testRequestException()
60920 {
60921 $e = new RequestException('Message');
60922 $request = new Request('GET', 'http://www.guzzle-project.com/');
60923 $e->setRequest($request);
60924 $this->assertEquals($request, $e->getRequest());
60925 }
60926
60927 /**
60928 * @covers Guzzle\Http\Exception\BadResponseException
60929 */
60930 public function testBadResponseException()
60931 {
60932 $e = new BadResponseException('Message');
60933 $response = new Response(200);
60934 $e->setResponse($response);
60935 $this->assertEquals($response, $e->getResponse());
60936 }
60937
60938 /**
60939 * @covers Guzzle\Http\Exception\BadResponseException::factory
60940 */
60941 public function testCreatesGenericErrorExceptionOnError()
60942 {
60943 $request = new Request('GET', 'http://www.example.com');
60944 $response = new Response(307);
60945 $e = BadResponseException::factory($request, $response);
60946 $this->assertInstanceOf('Guzzle\Http\Exception\BadResponseException', $e);
60947 }
60948
60949 /**
60950 * @covers Guzzle\Http\Exception\BadResponseException::factory
60951 */
60952 public function testCreatesClientErrorExceptionOnClientError()
60953 {
60954 $request = new Request('GET', 'http://www.example.com');
60955 $response = new Response(404);
60956 $e = BadResponseException::factory($request, $response);
60957 $this->assertInstanceOf('Guzzle\Http\Exception\ClientErrorResponseException', $e);
60958 }
60959
60960 /**
60961 * @covers Guzzle\Http\Exception\BadResponseException::factory
60962 */
60963 public function testCreatesServerErrorExceptionOnServerError()
60964 {
60965 $request = new Request('GET', 'http://www.example.com');
60966 $response = new Response(503);
60967 $e = BadResponseException::factory($request, $response);
60968 $this->assertInstanceOf('Guzzle\Http\Exception\ServerErrorResponseException', $e);
60969 }
60970 }
60971 <?php
60972
60973 namespace Guzzle\Tests\Http;
60974
60975 use Guzzle\Common\Collection;
60976 use Guzzle\Log\ClosureLogAdapter;
60977 use Guzzle\Parser\UriTemplate\UriTemplate;
60978 use Guzzle\Http\Message\Response;
60979 use Guzzle\Plugin\Log\LogPlugin;
60980 use Guzzle\Plugin\Mock\MockPlugin;
60981 use Guzzle\Http\Curl\CurlMulti;
60982 use Guzzle\Http\Client;
60983 use Guzzle\Common\Version;
60984
60985 /**
60986 * @group server
60987 * @covers Guzzle\Http\Client
60988 */
60989 class ClientTest extends \Guzzle\Tests\GuzzleTestCase
60990 {
60991 /**
60992 * @return LogPlugin
60993 */
60994 private function getLogPlugin()
60995 {
60996 return new LogPlugin(new ClosureLogAdapter(
60997 function($message, $priority, $extras = null) {
60998 echo $message . ' ' . $priority . ' ' . implode(' - ', (array) $extras) . "\n";
60999 }
61000 ));
61001 }
61002
61003 public function testAcceptsConfig()
61004 {
61005 $client = new Client('http://www.google.com/');
61006 $this->assertEquals('http://www.google.com/', $client->getBaseUrl());
61007 $this->assertSame($client, $client->setConfig(array(
61008 'test' => '123'
61009 )));
61010 $this->assertEquals(array('test' => '123'), $client->getConfig()->getAll());
61011 $this->assertEquals('123', $client->getConfig('test'));
61012 $this->assertSame($client, $client->setBaseUrl('http://www.test.com/{test}'));
61013 $this->assertEquals('http://www.test.com/123', $client->getBaseUrl());
61014 $this->assertEquals('http://www.test.com/{test}', $client->getBaseUrl(false));
61015
61016 try {
61017 $client->setConfig(false);
61018 } catch (\InvalidArgumentException $e) {
61019 }
61020 }
61021
61022 public function testDescribesEvents()
61023 {
61024 $this->assertEquals(array('client.create_request'), Client::getAllEvents());
61025 }
61026
61027 public function testConstructorCanAcceptConfig()
61028 {
61029 $client = new Client('http://www.test.com/', array(
61030 'data' => '123'
61031 ));
61032 $this->assertEquals('123', $client->getConfig('data'));
61033 }
61034
61035 public function testCanUseCollectionAsConfig()
61036 {
61037 $client = new Client('http://www.google.com/');
61038 $client->setConfig(new Collection(array(
61039 'api' => 'v1',
61040 'key' => 'value',
61041 'base_url' => 'http://www.google.com/'
61042 )));
61043 $this->assertEquals('v1', $client->getConfig('api'));
61044 }
61045
61046 public function testExpandsUriTemplatesUsingConfig()
61047 {
61048 $client = new Client('http://www.google.com/');
61049 $client->setConfig(array('api' => 'v1', 'key' => 'value', 'foo' => 'bar'));
61050 $ref = new \ReflectionMethod($client, 'expandTemplate');
61051 $ref->setAccessible(true);
61052 $this->assertEquals('Testing...api/v1/key/value', $ref->invoke($client, 'Testing...api/{api}/key/{key}'));
61053 }
61054
61055 public function testClientAttachersObserversToRequests()
61056 {
61057 $this->getServer()->flush();
61058 $this->getServer()->enqueue("HTTP/1.1 200 OK\r\nContent-Length: 0\r\n\r\n");
61059
61060 $client = new Client($this->getServer()->getUrl());
61061 $logPlugin = $this->getLogPlugin();
61062 $client->getEventDispatcher()->addSubscriber($logPlugin);
61063
61064 // Get a request from the client and ensure the the observer was
61065 // attached to the new request
61066 $request = $client->createRequest();
61067 $this->assertTrue($this->hasSubscriber($request, $logPlugin));
61068 }
61069
61070 public function testClientReturnsValidBaseUrls()
61071 {
61072 $client = new Client('http://www.{foo}.{data}/', array(
61073 'data' => '123',
61074 'foo' => 'bar'
61075 ));
61076 $this->assertEquals('http://www.bar.123/', $client->getBaseUrl());
61077 $client->setBaseUrl('http://www.google.com/');
61078 $this->assertEquals('http://www.google.com/', $client->getBaseUrl());
61079 }
61080
61081 public function testClientAddsCurlOptionsToRequests()
61082 {
61083 $client = new Client('http://www.test.com/', array(
61084 'api' => 'v1',
61085 // Adds the option using the curl values
61086 'curl.options' => array(
61087 'CURLOPT_HTTPAUTH' => 'CURLAUTH_DIGEST',
61088 'abc' => 'foo',
61089 'blacklist' => 'abc',
61090 'debug' => true
61091 )
61092 ));
61093
61094 $request = $client->createRequest();
61095 $options = $request->getCurlOptions();
61096 $this->assertEquals(CURLAUTH_DIGEST, $options->get(CURLOPT_HTTPAUTH));
61097 $this->assertEquals('foo', $options->get('abc'));
61098 $this->assertEquals('abc', $options->get('blacklist'));
61099 }
61100
61101 public function testClientAllowsFineGrainedSslControlButIsSecureByDefault()
61102 {
61103 $client = new Client('https://www.secure.com/');
61104
61105 // secure by default
61106 $request = $client->createRequest();
61107 $options = $request->getCurlOptions();
61108 $this->assertTrue($options->get(CURLOPT_SSL_VERIFYPEER));
61109
61110 // set a capath if you prefer
61111 $client = new Client('https://www.secure.com/');
61112 $client->setSslVerification(__DIR__);
61113 $request = $client->createRequest();
61114 $options = $request->getCurlOptions();
61115 $this->assertSame(__DIR__, $options->get(CURLOPT_CAPATH));
61116 }
61117
61118 public function testConfigSettingsControlSslConfiguration()
61119 {
61120 // Use the default ca certs on the system
61121 $client = new Client('https://www.secure.com/', array('ssl.certificate_authority' => 'system'));
61122 $this->assertNull($client->getConfig('curl.options'));
61123 // Can set the cacert value as well
61124 $client = new Client('https://www.secure.com/', array('ssl.certificate_authority' => false));
61125 $options = $client->getConfig('curl.options');
61126 $this->assertArrayNotHasKey(CURLOPT_CAINFO, $options);
61127 $this->assertSame(false, $options[CURLOPT_SSL_VERIFYPEER]);
61128 $this->assertSame(2, $options[CURLOPT_SSL_VERIFYHOST]);
61129 }
61130
61131 public function testClientAllowsUnsafeOperationIfRequested()
61132 {
61133 // be really unsafe if you insist
61134 $client = new Client('https://www.secure.com/', array(
61135 'api' => 'v1'
61136 ));
61137
61138 $client->setSslVerification(false);
61139 $request = $client->createRequest();
61140 $options = $request->getCurlOptions();
61141 $this->assertFalse($options->get(CURLOPT_SSL_VERIFYPEER));
61142 $this->assertNull($options->get(CURLOPT_CAINFO));
61143 }
61144
61145 /**
61146 * @expectedException \Guzzle\Common\Exception\RuntimeException
61147 */
61148 public function testThrowsExceptionForInvalidCertificate()
61149 {
61150 $client = new Client('https://www.secure.com/');
61151 $client->setSslVerification('/path/to/missing/file');
61152 }
61153
61154 public function testClientAllowsSettingSpecificSslCaInfo()
61155 {
61156 // set a file other than the provided cacert.pem
61157 $client = new Client('https://www.secure.com/', array(
61158 'api' => 'v1'
61159 ));
61160
61161 $client->setSslVerification(__FILE__);
61162 $request = $client->createRequest();
61163 $options = $request->getCurlOptions();
61164 $this->assertSame(__FILE__, $options->get(CURLOPT_CAINFO));
61165 }
61166
61167 /**
61168 * @expectedException Guzzle\Common\Exception\InvalidArgumentException
61169 */
61170 public function testClientPreventsInadvertentInsecureVerifyHostSetting()
61171 {
61172 // set a file other than the provided cacert.pem
61173 $client = new Client('https://www.secure.com/', array(
61174 'api' => 'v1'
61175 ));
61176 $client->setSslVerification(__FILE__, true, true);
61177 }
61178
61179 /**
61180 * @expectedException Guzzle\Common\Exception\InvalidArgumentException
61181 */
61182 public function testClientPreventsInvalidVerifyPeerSetting()
61183 {
61184 // set a file other than the provided cacert.pem
61185 $client = new Client('https://www.secure.com/', array(
61186 'api' => 'v1'
61187 ));
61188 $client->setSslVerification(__FILE__, 'yes');
61189 }
61190
61191 public function testClientAddsParamsToRequests()
61192 {
61193 Version::$emitWarnings = false;
61194 $client = new Client('http://www.example.com', array(
61195 'api' => 'v1',
61196 'request.params' => array(
61197 'foo' => 'bar',
61198 'baz' => 'jar'
61199 )
61200 ));
61201 $request = $client->createRequest();
61202 $this->assertEquals('bar', $request->getParams()->get('foo'));
61203 $this->assertEquals('jar', $request->getParams()->get('baz'));
61204 Version::$emitWarnings = true;
61205 }
61206
61207 public function urlProvider()
61208 {
61209 $u = $this->getServer()->getUrl() . 'base/';
61210 $u2 = $this->getServer()->getUrl() . 'base?z=1';
61211 return array(
61212 array($u, '', $u),
61213 array($u, 'relative/path/to/resource', $u . 'relative/path/to/resource'),
61214 array($u, 'relative/path/to/resource?a=b&c=d', $u . 'relative/path/to/resource?a=b&c=d'),
61215 array($u, '/absolute/path/to/resource', $this->getServer()->getUrl() . 'absolute/path/to/resource'),
61216 array($u, '/absolute/path/to/resource?a=b&c=d', $this->getServer()->getUrl() . 'absolute/path/to/resource?a=b&c=d'),
61217 array($u2, '/absolute/path/to/resource?a=b&c=d', $this->getServer()->getUrl() . 'absolute/path/to/resource?a=b&c=d'),
61218 array($u2, 'relative/path/to/resource', $this->getServer()->getUrl() . 'base/relative/path/to/resource'),
61219 array($u2, 'relative/path/to/resource?another=query', $this->getServer()->getUrl() . 'base/relative/path/to/resource?another=query')
61220 );
61221 }
61222
61223 /**
61224 * @dataProvider urlProvider
61225 */
61226 public function testBuildsRelativeUrls($baseUrl, $url, $result)
61227 {
61228 $client = new Client($baseUrl);
61229 $this->assertEquals($client->get($url)->getUrl(), $result);
61230 }
61231
61232 public function testAllowsConfigsToBeChangedAndInjectedInBaseUrl()
61233 {
61234 $client = new Client('http://{a}/{b}');
61235 $this->assertEquals('http:///', $client->getBaseUrl());
61236 $this->assertEquals('http://{a}/{b}', $client->getBaseUrl(false));
61237 $client->setConfig(array(
61238 'a' => 'test.com',
61239 'b' => 'index.html'
61240 ));
61241 $this->assertEquals('http://test.com/index.html', $client->getBaseUrl());
61242 }
61243
61244 public function testCreatesRequestsWithDefaultValues()
61245 {
61246 $client = new Client($this->getServer()->getUrl() . 'base');
61247
61248 // Create a GET request
61249 $request = $client->createRequest();
61250 $this->assertEquals('GET', $request->getMethod());
61251 $this->assertEquals($client->getBaseUrl(), $request->getUrl());
61252
61253 // Create a DELETE request
61254 $request = $client->createRequest('DELETE');
61255 $this->assertEquals('DELETE', $request->getMethod());
61256 $this->assertEquals($client->getBaseUrl(), $request->getUrl());
61257
61258 // Create a HEAD request with custom headers
61259 $request = $client->createRequest('HEAD', 'http://www.test.com/');
61260 $this->assertEquals('HEAD', $request->getMethod());
61261 $this->assertEquals('http://www.test.com/', $request->getUrl());
61262
61263 // Create a PUT request
61264 $request = $client->createRequest('PUT');
61265 $this->assertEquals('PUT', $request->getMethod());
61266
61267 // Create a PUT request with injected config
61268 $client->getConfig()->set('a', 1)->set('b', 2);
61269 $request = $client->createRequest('PUT', '/path/{a}?q={b}');
61270 $this->assertEquals($request->getUrl(), $this->getServer()->getUrl() . 'path/1?q=2');
61271 }
61272
61273 public function testClientHasHelperMethodsForCreatingRequests()
61274 {
61275 $url = $this->getServer()->getUrl();
61276 $client = new Client($url . 'base');
61277 $this->assertEquals('GET', $client->get()->getMethod());
61278 $this->assertEquals('PUT', $client->put()->getMethod());
61279 $this->assertEquals('POST', $client->post()->getMethod());
61280 $this->assertEquals('HEAD', $client->head()->getMethod());
61281 $this->assertEquals('DELETE', $client->delete()->getMethod());
61282 $this->assertEquals('OPTIONS', $client->options()->getMethod());
61283 $this->assertEquals('PATCH', $client->patch()->getMethod());
61284 $this->assertEquals($url . 'base/abc', $client->get('abc')->getUrl());
61285 $this->assertEquals($url . 'zxy', $client->put('/zxy')->getUrl());
61286 $this->assertEquals($url . 'zxy?a=b', $client->post('/zxy?a=b')->getUrl());
61287 $this->assertEquals($url . 'base?a=b', $client->head('?a=b')->getUrl());
61288 $this->assertEquals($url . 'base?a=b', $client->delete('/base?a=b')->getUrl());
61289 }
61290
61291 public function testClientInjectsConfigsIntoUrls()
61292 {
61293 $client = new Client('http://www.test.com/api/v1', array(
61294 'test' => '123'
61295 ));
61296 $request = $client->get('relative/{test}');
61297 $this->assertEquals('http://www.test.com/api/v1/relative/123', $request->getUrl());
61298 }
61299
61300 public function testAllowsEmptyBaseUrl()
61301 {
61302 $client = new Client();
61303 $request = $client->get('http://www.google.com/');
61304 $this->assertEquals('http://www.google.com/', $request->getUrl());
61305 $request->setResponse(new Response(200), true);
61306 $request->send();
61307 }
61308
61309 public function testAllowsCustomCurlMultiObjects()
61310 {
61311 $mock = $this->getMock('Guzzle\\Http\\Curl\\CurlMulti', array('add', 'send'));
61312 $mock->expects($this->once())
61313 ->method('add')
61314 ->will($this->returnSelf());
61315 $mock->expects($this->once())
61316 ->method('send')
61317 ->will($this->returnSelf());
61318
61319 $client = new Client();
61320 $client->setCurlMulti($mock);
61321
61322 $request = $client->get();
61323 $request->setResponse(new Response(200), true);
61324 $client->send($request);
61325 }
61326
61327 public function testClientSendsMultipleRequests()
61328 {
61329 $client = new Client($this->getServer()->getUrl());
61330 $mock = new MockPlugin();
61331
61332 $responses = array(
61333 new Response(200),
61334 new Response(201),
61335 new Response(202)
61336 );
61337
61338 $mock->addResponse($responses[0]);
61339 $mock->addResponse($responses[1]);
61340 $mock->addResponse($responses[2]);
61341
61342 $client->getEventDispatcher()->addSubscriber($mock);
61343
61344 $requests = array(
61345 $client->get(),
61346 $client->head(),
61347 $client->put('/', null, 'test')
61348 );
61349
61350 $this->assertEquals(array(
61351 $responses[0],
61352 $responses[1],
61353 $responses[2]
61354 ), $client->send($requests));
61355 }
61356
61357 public function testClientSendsSingleRequest()
61358 {
61359 $client = new Client($this->getServer()->getUrl());
61360 $mock = new MockPlugin();
61361 $response = new Response(200);
61362 $mock->addResponse($response);
61363 $client->getEventDispatcher()->addSubscriber($mock);
61364 $this->assertEquals($response, $client->send($client->get()));
61365 }
61366
61367 /**
61368 * @expectedException \Guzzle\Http\Exception\BadResponseException
61369 */
61370 public function testClientThrowsExceptionForSingleRequest()
61371 {
61372 $client = new Client($this->getServer()->getUrl());
61373 $mock = new MockPlugin();
61374 $response = new Response(404);
61375 $mock->addResponse($response);
61376 $client->getEventDispatcher()->addSubscriber($mock);
61377 $client->send($client->get());
61378 }
61379
61380 /**
61381 * @expectedException \Guzzle\Common\Exception\ExceptionCollection
61382 */
61383 public function testClientThrowsExceptionForMultipleRequests()
61384 {
61385 $client = new Client($this->getServer()->getUrl());
61386 $mock = new MockPlugin();
61387 $mock->addResponse(new Response(200));
61388 $mock->addResponse(new Response(404));
61389 $client->getEventDispatcher()->addSubscriber($mock);
61390 $client->send(array($client->get(), $client->head()));
61391 }
61392
61393 public function testQueryStringsAreNotDoubleEncoded()
61394 {
61395 $client = new Client('http://test.com', array(
61396 'path' => array('foo', 'bar'),
61397 'query' => 'hi there',
61398 'data' => array(
61399 'test' => 'a&b'
61400 )
61401 ));
61402
61403 $request = $client->get('{/path*}{?query,data*}');
61404 $this->assertEquals('http://test.com/foo/bar?query=hi%20there&test=a%26b', $request->getUrl());
61405 $this->assertEquals('hi there', $request->getQuery()->get('query'));
61406 $this->assertEquals('a&b', $request->getQuery()->get('test'));
61407 }
61408
61409 public function testQueryStringsAreNotDoubleEncodedUsingAbsolutePaths()
61410 {
61411 $client = new Client('http://test.com', array(
61412 'path' => array('foo', 'bar'),
61413 'query' => 'hi there',
61414 ));
61415 $request = $client->get('http://test.com{?query}');
61416 $this->assertEquals('http://test.com?query=hi%20there', $request->getUrl());
61417 $this->assertEquals('hi there', $request->getQuery()->get('query'));
61418 }
61419
61420 public function testAllowsUriTemplateInjection()
61421 {
61422 $client = new Client('http://test.com');
61423 $ref = new \ReflectionMethod($client, 'getUriTemplate');
61424 $ref->setAccessible(true);
61425 $a = $ref->invoke($client);
61426 $this->assertSame($a, $ref->invoke($client));
61427 $client->setUriTemplate(new UriTemplate());
61428 $this->assertNotSame($a, $ref->invoke($client));
61429 }
61430
61431 public function testAllowsCustomVariablesWhenExpandingTemplates()
61432 {
61433 $client = new Client('http://test.com', array('test' => 'hi'));
61434 $ref = new \ReflectionMethod($client, 'expandTemplate');
61435 $ref->setAccessible(true);
61436 $uri = $ref->invoke($client, 'http://{test}{?query*}', array('query' => array('han' => 'solo')));
61437 $this->assertEquals('http://hi?han=solo', $uri);
61438 }
61439
61440 public function testUriArrayAllowsCustomTemplateVariables()
61441 {
61442 $client = new Client();
61443 $vars = array(
61444 'var' => 'hi'
61445 );
61446 $this->assertEquals('/hi', (string) $client->createRequest('GET', array('/{var}', $vars))->getUrl());
61447 $this->assertEquals('/hi', (string) $client->get(array('/{var}', $vars))->getUrl());
61448 $this->assertEquals('/hi', (string) $client->put(array('/{var}', $vars))->getUrl());
61449 $this->assertEquals('/hi', (string) $client->post(array('/{var}', $vars))->getUrl());
61450 $this->assertEquals('/hi', (string) $client->head(array('/{var}', $vars))->getUrl());
61451 $this->assertEquals('/hi', (string) $client->options(array('/{var}', $vars))->getUrl());
61452 }
61453
61454 public function testAllowsDefaultHeaders()
61455 {
61456 Version::$emitWarnings = false;
61457 $default = array('X-Test' => 'Hi!');
61458 $other = array('X-Other' => 'Foo');
61459
61460 $client = new Client();
61461 $client->setDefaultHeaders($default);
61462 $this->assertEquals($default, $client->getDefaultHeaders()->getAll());
61463 $client->setDefaultHeaders(new Collection($default));
61464 $this->assertEquals($default, $client->getDefaultHeaders()->getAll());
61465
61466 $request = $client->createRequest('GET', null, $other);
61467 $this->assertEquals('Hi!', $request->getHeader('X-Test'));
61468 $this->assertEquals('Foo', $request->getHeader('X-Other'));
61469
61470 $request = $client->createRequest('GET', null, new Collection($other));
61471 $this->assertEquals('Hi!', $request->getHeader('X-Test'));
61472 $this->assertEquals('Foo', $request->getHeader('X-Other'));
61473
61474 $request = $client->createRequest('GET');
61475 $this->assertEquals('Hi!', $request->getHeader('X-Test'));
61476 Version::$emitWarnings = true;
61477 }
61478
61479 public function testDontReuseCurlMulti()
61480 {
61481 $client1 = new Client();
61482 $client2 = new Client();
61483 $this->assertNotSame($client1->getCurlMulti(), $client2->getCurlMulti());
61484 }
61485
61486 public function testGetDefaultUserAgent()
61487 {
61488 $client = new Client();
61489 $agent = $this->readAttribute($client, 'userAgent');
61490 $version = curl_version();
61491 $testAgent = sprintf('Guzzle/%s curl/%s PHP/%s', Version::VERSION, $version['version'], PHP_VERSION);
61492 $this->assertEquals($agent, $testAgent);
61493
61494 $client->setUserAgent('foo');
61495 $this->assertEquals('foo', $this->readAttribute($client, 'userAgent'));
61496 }
61497
61498 public function testOverwritesUserAgent()
61499 {
61500 $client = new Client();
61501 $request = $client->createRequest('GET', 'http://www.foo.com', array('User-agent' => 'foo'));
61502 $this->assertEquals('foo', (string) $request->getHeader('User-Agent'));
61503 }
61504
61505 public function testUsesDefaultUserAgent()
61506 {
61507 $client = new Client();
61508 $request = $client->createRequest('GET', 'http://www.foo.com');
61509 $this->assertContains('Guzzle/', (string) $request->getHeader('User-Agent'));
61510 }
61511
61512 public function testCanSetDefaultRequestOptions()
61513 {
61514 $client = new Client();
61515 $client->getConfig()->set('request.options', array(
61516 'query' => array('test' => '123', 'other' => 'abc'),
61517 'headers' => array('Foo' => 'Bar', 'Baz' => 'Bam')
61518 ));
61519 $request = $client->createRequest('GET', 'http://www.foo.com?test=hello', array('Foo' => 'Test'));
61520 // Explicit options on a request should overrule default options
61521 $this->assertEquals('Test', (string) $request->getHeader('Foo'));
61522 $this->assertEquals('hello', $request->getQuery()->get('test'));
61523 // Default options should still be set
61524 $this->assertEquals('abc', $request->getQuery()->get('other'));
61525 $this->assertEquals('Bam', (string) $request->getHeader('Baz'));
61526 }
61527
61528 public function testCanSetSetOptionsOnRequests()
61529 {
61530 $client = new Client();
61531 $request = $client->createRequest('GET', 'http://www.foo.com?test=hello', array('Foo' => 'Test'), null, array(
61532 'cookies' => array('michael' => 'test')
61533 ));
61534 $this->assertEquals('test', $request->getCookie('michael'));
61535 }
61536
61537 public function testHasDefaultOptionsHelperMethods()
61538 {
61539 $client = new Client();
61540 // With path
61541 $client->setDefaultOption('headers/foo', 'bar');
61542 $this->assertEquals('bar', $client->getDefaultOption('headers/foo'));
61543 // With simple key
61544 $client->setDefaultOption('allow_redirects', false);
61545 $this->assertFalse($client->getDefaultOption('allow_redirects'));
61546
61547 $this->assertEquals(array(
61548 'headers' => array('foo' => 'bar'),
61549 'allow_redirects' => false
61550 ), $client->getConfig('request.options'));
61551
61552 $request = $client->get('/');
61553 $this->assertEquals('bar', $request->getHeader('foo'));
61554 }
61555
61556 public function testHeadCanUseOptions()
61557 {
61558 $client = new Client();
61559 $head = $client->head('http://www.foo.com', array(), array('query' => array('foo' => 'bar')));
61560 $this->assertEquals('bar', $head->getQuery()->get('foo'));
61561 }
61562 }
61563 <?php
61564
61565 namespace Guzzle\Tests\Http;
61566
61567 use Guzzle\Http\QueryString;
61568 use Guzzle\Http\Url;
61569
61570 /**
61571 * @covers Guzzle\Http\Url
61572 */
61573 class UrlTest extends \Guzzle\Tests\GuzzleTestCase
61574 {
61575 public function testEmptyUrl()
61576 {
61577 $url = Url::factory('');
61578 $this->assertEquals('', (string) $url);
61579 }
61580
61581 public function testPortIsDeterminedFromScheme()
61582 {
61583 $this->assertEquals(80, Url::factory('http://www.test.com/')->getPort());
61584 $this->assertEquals(443, Url::factory('https://www.test.com/')->getPort());
61585 $this->assertEquals(null, Url::factory('ftp://www.test.com/')->getPort());
61586 $this->assertEquals(8192, Url::factory('http://www.test.com:8192/')->getPort());
61587 }
61588
61589 public function testCloneCreatesNewInternalObjects()
61590 {
61591 $u1 = Url::factory('http://www.test.com/');
61592 $u2 = clone $u1;
61593 $this->assertNotSame($u1->getQuery(), $u2->getQuery());
61594 }
61595
61596 public function testValidatesUrlPartsInFactory()
61597 {
61598 $url = Url::factory('/index.php');
61599 $this->assertEquals('/index.php', (string) $url);
61600 $this->assertFalse($url->isAbsolute());
61601
61602 $url = 'http://michael:test@test.com:80/path/123?q=abc#test';
61603 $u = Url::factory($url);
61604 $this->assertEquals('http://michael:test@test.com/path/123?q=abc#test', (string) $u);
61605 $this->assertTrue($u->isAbsolute());
61606 }
61607
61608 public function testAllowsFalsyUrlParts()
61609 {
61610 $url = Url::factory('http://0:50/0?0#0');
61611 $this->assertSame('0', $url->getHost());
61612 $this->assertEquals(50, $url->getPort());
61613 $this->assertSame('/0', $url->getPath());
61614 $this->assertEquals('0=', (string) $url->getQuery());
61615 $this->assertSame('0', $url->getFragment());
61616 $this->assertEquals('http://0:50/0?0=#0', (string) $url);
61617
61618 $url = Url::factory('');
61619 $this->assertSame('', (string) $url);
61620
61621 $url = Url::factory('0');
61622 $this->assertSame('0', (string) $url);
61623 }
61624
61625 public function testBuildsRelativeUrlsWithFalsyParts()
61626 {
61627 $url = Url::buildUrl(array(
61628 'host' => '0',
61629 'path' => '0',
61630 ));
61631
61632 $this->assertSame('//0/0', $url);
61633
61634 $url = Url::buildUrl(array(
61635 'path' => '0',
61636 ));
61637 $this->assertSame('0', $url);
61638 }
61639
61640 public function testUrlStoresParts()
61641 {
61642 $url = Url::factory('http://test:pass@www.test.com:8081/path/path2/?a=1&b=2#fragment');
61643 $this->assertEquals('http', $url->getScheme());
61644 $this->assertEquals('test', $url->getUsername());
61645 $this->assertEquals('pass', $url->getPassword());
61646 $this->assertEquals('www.test.com', $url->getHost());
61647 $this->assertEquals(8081, $url->getPort());
61648 $this->assertEquals('/path/path2/', $url->getPath());
61649 $this->assertEquals('fragment', $url->getFragment());
61650 $this->assertEquals('a=1&b=2', (string) $url->getQuery());
61651
61652 $this->assertEquals(array(
61653 'fragment' => 'fragment',
61654 'host' => 'www.test.com',
61655 'pass' => 'pass',
61656 'path' => '/path/path2/',
61657 'port' => 8081,
61658 'query' => 'a=1&b=2',
61659 'scheme' => 'http',
61660 'user' => 'test'
61661 ), $url->getParts());
61662 }
61663
61664 public function testHandlesPathsCorrectly()
61665 {
61666 $url = Url::factory('http://www.test.com');
61667 $this->assertEquals('', $url->getPath());
61668 $url->setPath('test');
61669 $this->assertEquals('test', $url->getPath());
61670
61671 $url->setPath('/test/123/abc');
61672 $this->assertEquals(array('test', '123', 'abc'), $url->getPathSegments());
61673
61674 $parts = parse_url('http://www.test.com/test');
61675 $parts['path'] = '';
61676 $this->assertEquals('http://www.test.com', Url::buildUrl($parts));
61677 $parts['path'] = 'test';
61678 $this->assertEquals('http://www.test.com/test', Url::buildUrl($parts));
61679 }
61680
61681 public function testAddsQueryStringIfPresent()
61682 {
61683 $this->assertEquals('?foo=bar', Url::buildUrl(array(
61684 'query' => 'foo=bar'
61685 )));
61686 }
61687
61688 public function testAddsToPath()
61689 {
61690 // Does nothing here
61691 $this->assertEquals('http://e.com/base?a=1', (string) Url::factory('http://e.com/base?a=1')->addPath(false));
61692 $this->assertEquals('http://e.com/base?a=1', (string) Url::factory('http://e.com/base?a=1')->addPath(''));
61693 $this->assertEquals('http://e.com/base?a=1', (string) Url::factory('http://e.com/base?a=1')->addPath('/'));
61694
61695 $this->assertEquals('http://e.com/base/relative?a=1', (string) Url::factory('http://e.com/base?a=1')->addPath('relative'));
61696 $this->assertEquals('http://e.com/base/relative?a=1', (string) Url::factory('http://e.com/base?a=1')->addPath('/relative'));
61697 }
61698
61699 /**
61700 * URL combination data provider
61701 *
61702 * @return array
61703 */
61704 public function urlCombineDataProvider()
61705 {
61706 return array(
61707 array('http://www.example.com/', 'http://www.example.com/', 'http://www.example.com/'),
61708 array('http://www.example.com/path', '/absolute', 'http://www.example.com/absolute'),
61709 array('http://www.example.com/path', '/absolute?q=2', 'http://www.example.com/absolute?q=2'),
61710 array('http://www.example.com/path', 'more', 'http://www.example.com/path/more'),
61711 array('http://www.example.com/path', 'more?q=1', 'http://www.example.com/path/more?q=1'),
61712 array('http://www.example.com/', '?q=1', 'http://www.example.com/?q=1'),
61713 array('http://www.example.com/path', 'http://test.com', 'http://test.com'),
61714 array('http://www.example.com:8080/path', 'http://test.com', 'http://test.com'),
61715 array('http://www.example.com:8080/path', '?q=2#abc', 'http://www.example.com:8080/path?q=2#abc'),
61716 array('http://u:a@www.example.com/path', 'test', 'http://u:a@www.example.com/path/test'),
61717 array('http://www.example.com/path', 'http://u:a@www.example.com/', 'http://u:a@www.example.com/'),
61718 array('/path?q=2', 'http://www.test.com/', 'http://www.test.com/path?q=2'),
61719 array('http://api.flickr.com/services/', 'http://www.flickr.com/services/oauth/access_token', 'http://www.flickr.com/services/oauth/access_token')
61720 );
61721 }
61722
61723 /**
61724 * @dataProvider urlCombineDataProvider
61725 */
61726 public function testCombinesUrls($a, $b, $c)
61727 {
61728 $this->assertEquals($c, (string) Url::factory($a)->combine($b));
61729 }
61730
61731 public function testHasGettersAndSetters()
61732 {
61733 $url = Url::factory('http://www.test.com/');
61734 $this->assertEquals('example.com', $url->setHost('example.com')->getHost());
61735 $this->assertEquals('8080', $url->setPort(8080)->getPort());
61736 $this->assertEquals('/foo/bar', $url->setPath(array('foo', 'bar'))->getPath());
61737 $this->assertEquals('a', $url->setPassword('a')->getPassword());
61738 $this->assertEquals('b', $url->setUsername('b')->getUsername());
61739 $this->assertEquals('abc', $url->setFragment('abc')->getFragment());
61740 $this->assertEquals('https', $url->setScheme('https')->getScheme());
61741 $this->assertEquals('a=123', (string) $url->setQuery('a=123')->getQuery());
61742 $this->assertEquals('https://b:a@example.com:8080/foo/bar?a=123#abc', (string) $url);
61743 $this->assertEquals('b=boo', (string) $url->setQuery(new QueryString(array(
61744 'b' => 'boo'
61745 )))->getQuery());
61746 $this->assertEquals('https://b:a@example.com:8080/foo/bar?b=boo#abc', (string) $url);
61747 }
61748
61749 public function testSetQueryAcceptsArray()
61750 {
61751 $url = Url::factory('http://www.test.com');
61752 $url->setQuery(array('a' => 'b'));
61753 $this->assertEquals('http://www.test.com?a=b', (string) $url);
61754 }
61755
61756 public function urlProvider()
61757 {
61758 return array(
61759 array('/foo/..', ''),
61760 array('//foo//..', ''),
61761 array('/foo/../..', ''),
61762 array('/foo/../.', ''),
61763 array('/./foo/..', ''),
61764 array('/./foo', '/foo'),
61765 array('/./foo/', '/foo/'),
61766 array('/./foo/bar/baz/pho/../..', 'foo/bar'),
61767 array('*', '*'),
61768 array('/foo', '/foo')
61769 );
61770 }
61771
61772 /**
61773 * @dataProvider urlProvider
61774 */
61775 public function testNormalizesPaths($path, $result)
61776 {
61777 $url = Url::factory('http://www.example.com/');
61778 $url->setPath($path)->normalizePath();
61779 $this->assertEquals($result, $url->getPath());
61780 }
61781
61782 public function testSettingHostWithPortModifiesPort()
61783 {
61784 $url = Url::factory('http://www.example.com');
61785 $url->setHost('foo:8983');
61786 $this->assertEquals('foo', $url->getHost());
61787 $this->assertEquals(8983, $url->getPort());
61788 }
61789
61790 /**
61791 * @expectedException \Guzzle\Common\Exception\InvalidArgumentException
61792 */
61793 public function testValidatesUrlCanBeParsed()
61794 {
61795 Url::factory('foo:////');
61796 }
61797
61798 public function testConvertsSpecialCharsInPathWhenCastingToString()
61799 {
61800 $url = Url::factory('http://foo.com/baz bar?a=b');
61801 $url->addPath('?');
61802 $this->assertEquals('http://foo.com/baz%20bar/%3F?a=b', (string) $url);
61803 }
61804 }
61805 <?php
61806
61807 namespace Guzzle\Tests\Http;
61808
61809 use Guzzle\Http\Mimetypes;
61810
61811 /**
61812 * @covers Guzzle\Http\Mimetypes
61813 */
61814 class MimetypesTest extends \Guzzle\Tests\GuzzleTestCase
61815 {
61816 public function testGetsFromExtension()
61817 {
61818 $this->assertEquals('text/x-php', Mimetypes::getInstance()->fromExtension('php'));
61819 }
61820
61821 public function testGetsFromFilename()
61822 {
61823 $this->assertEquals('text/x-php', Mimetypes::getInstance()->fromFilename(__FILE__));
61824 }
61825
61826 public function testGetsFromCaseInsensitiveFilename()
61827 {
61828 $this->assertEquals('text/x-php', Mimetypes::getInstance()->fromFilename(strtoupper(__FILE__)));
61829 }
61830
61831 public function testReturnsNullWhenNoMatchFound()
61832 {
61833 $this->assertNull(Mimetypes::getInstance()->fromExtension('foobar'));
61834 }
61835 }
61836 <?php
61837
61838 namespace Guzzle\Tests\Http;
61839
61840 use Guzzle\Http\EntityBody;
61841 use Guzzle\Http\QueryString;
61842
61843 /**
61844 * @group server
61845 * @covers Guzzle\Http\EntityBody
61846 */
61847 class EntityBodyTest extends \Guzzle\Tests\GuzzleTestCase
61848 {
61849 /**
61850 * @expectedException \Guzzle\Common\Exception\InvalidArgumentException
61851 */
61852 public function testFactoryThrowsException()
61853 {
61854 $body = EntityBody::factory(false);
61855 }
61856
61857 public function testFactory()
61858 {
61859 $body = EntityBody::factory('data');
61860 $this->assertEquals('data', (string) $body);
61861 $this->assertEquals(4, $body->getContentLength());
61862 $this->assertEquals('PHP', $body->getWrapper());
61863 $this->assertEquals('TEMP', $body->getStreamType());
61864
61865 $handle = fopen(__DIR__ . '/../../../../phpunit.xml.dist', 'r');
61866 if (!$handle) {
61867 $this->fail('Could not open test file');
61868 }
61869 $body = EntityBody::factory($handle);
61870 $this->assertEquals(__DIR__ . '/../../../../phpunit.xml.dist', $body->getUri());
61871 $this->assertTrue($body->isLocal());
61872 $this->assertEquals(__DIR__ . '/../../../../phpunit.xml.dist', $body->getUri());
61873 $this->assertEquals(filesize(__DIR__ . '/../../../../phpunit.xml.dist'), $body->getContentLength());
61874
61875 // make sure that a body will return as the same object
61876 $this->assertTrue($body === EntityBody::factory($body));
61877 }
61878
61879 public function testFactoryCreatesTempStreamByDefault()
61880 {
61881 $body = EntityBody::factory('');
61882 $this->assertEquals('PHP', $body->getWrapper());
61883 $this->assertEquals('TEMP', $body->getStreamType());
61884 $body = EntityBody::factory();
61885 $this->assertEquals('PHP', $body->getWrapper());
61886 $this->assertEquals('TEMP', $body->getStreamType());
61887 }
61888
61889 public function testFactoryCanCreateFromObject()
61890 {
61891 $body = EntityBody::factory(new QueryString(array('foo' => 'bar')));
61892 $this->assertEquals('foo=bar', (string) $body);
61893 }
61894
61895 /**
61896 * @expectedException \Guzzle\Common\Exception\InvalidArgumentException
61897 */
61898 public function testFactoryEnsuresObjectsHaveToStringMethod()
61899 {
61900 EntityBody::factory(new \stdClass('a'));
61901 }
61902
61903 public function testHandlesCompression()
61904 {
61905 $body = EntityBody::factory('testing 123...testing 123');
61906 $this->assertFalse($body->getContentEncoding(), '-> getContentEncoding() must initially return FALSE');
61907 $size = $body->getContentLength();
61908 $body->compress();
61909 $this->assertEquals('gzip', $body->getContentEncoding(), '-> getContentEncoding() must return the correct encoding after compressing');
61910 $this->assertEquals(gzdeflate('testing 123...testing 123'), (string) $body);
61911 $this->assertTrue($body->getContentLength() < $size);
61912 $this->assertTrue($body->uncompress());
61913 $this->assertEquals('testing 123...testing 123', (string) $body);
61914 $this->assertFalse($body->getContentEncoding(), '-> getContentEncoding() must reset to FALSE');
61915
61916 if (in_array('bzip2.*', stream_get_filters())) {
61917 $this->assertTrue($body->compress('bzip2.compress'));
61918 $this->assertEquals('compress', $body->getContentEncoding(), '-> compress() must set \'compress\' as the Content-Encoding');
61919 }
61920
61921 $this->assertFalse($body->compress('non-existent'), '-> compress() must return false when a non-existent stream filter is used');
61922
61923 // Release the body
61924 unset($body);
61925
61926 // Use gzip compression on the initial content. This will include a
61927 // gzip header which will need to be stripped when deflating the stream
61928 $body = EntityBody::factory(gzencode('test'));
61929 $this->assertSame($body, $body->setStreamFilterContentEncoding('zlib.deflate'));
61930 $this->assertTrue($body->uncompress('zlib.inflate'));
61931 $this->assertEquals('test', (string) $body);
61932 unset($body);
61933
61934 // Test using a very long string
61935 $largeString = '';
61936 for ($i = 0; $i < 25000; $i++) {
61937 $largeString .= chr(rand(33, 126));
61938 }
61939 $body = EntityBody::factory($largeString);
61940 $this->assertEquals($largeString, (string) $body);
61941 $this->assertTrue($body->compress());
61942 $this->assertNotEquals($largeString, (string) $body);
61943 $compressed = (string) $body;
61944 $this->assertTrue($body->uncompress());
61945 $this->assertEquals($largeString, (string) $body);
61946 $this->assertEquals($compressed, gzdeflate($largeString));
61947
61948 $body = EntityBody::factory(fopen(__DIR__ . '/../TestData/compress_test', 'w'));
61949 $this->assertFalse($body->compress());
61950 unset($body);
61951
61952 unlink(__DIR__ . '/../TestData/compress_test');
61953 }
61954
61955 public function testDeterminesContentType()
61956 {
61957 // Test using a string/temp stream
61958 $body = EntityBody::factory('testing 123...testing 123');
61959 $this->assertNull($body->getContentType());
61960
61961 // Use a local file
61962 $body = EntityBody::factory(fopen(__FILE__, 'r'));
61963 $this->assertContains('text/x-', $body->getContentType());
61964 }
61965
61966 public function testCreatesMd5Checksum()
61967 {
61968 $body = EntityBody::factory('testing 123...testing 123');
61969 $this->assertEquals(md5('testing 123...testing 123'), $body->getContentMd5());
61970
61971 $server = $this->getServer()->enqueue(
61972 "HTTP/1.1 200 OK" . "\r\n" .
61973 "Content-Length: 3" . "\r\n\r\n" .
61974 "abc"
61975 );
61976
61977 $body = EntityBody::factory(fopen($this->getServer()->getUrl(), 'r'));
61978 $this->assertFalse($body->getContentMd5());
61979 }
61980
61981 public function testSeeksToOriginalPosAfterMd5()
61982 {
61983 $body = EntityBody::factory('testing 123');
61984 $body->seek(4);
61985 $this->assertEquals(md5('testing 123'), $body->getContentMd5());
61986 $this->assertEquals(4, $body->ftell());
61987 $this->assertEquals('ing 123', $body->read(1000));
61988 }
61989
61990 public function testGetTypeFormBodyFactoring()
61991 {
61992 $body = EntityBody::factory(array('key1' => 'val1', 'key2' => 'val2'));
61993 $this->assertEquals('key1=val1&key2=val2', (string) $body);
61994 }
61995
61996 public function testAllowsCustomRewind()
61997 {
61998 $body = EntityBody::factory('foo');
61999 $rewound = false;
62000 $body->setRewindFunction(function ($body) use (&$rewound) {
62001 $rewound = true;
62002 return $body->seek(0);
62003 });
62004 $body->seek(2);
62005 $this->assertTrue($body->rewind());
62006 $this->assertTrue($rewound);
62007 }
62008
62009 /**
62010 * @expectedException \Guzzle\Common\Exception\InvalidArgumentException
62011 */
62012 public function testCustomRewindFunctionMustBeCallable()
62013 {
62014 $body = EntityBody::factory();
62015 $body->setRewindFunction('foo');
62016 }
62017 }
62018 <?php
62019
62020 namespace Guzzle\Tests\Mock;
62021
62022 class MockMulti extends \Guzzle\Http\Curl\CurlMulti
62023 {
62024 public function getHandle()
62025 {
62026 return $this->multiHandle;
62027 }
62028 }
62029 <?php
62030
62031 namespace Guzzle\Tests\Mock;
62032
62033 use Guzzle\Service\Command\ResponseClassInterface;
62034 use Guzzle\Service\Command\OperationCommand;
62035
62036 class CustomResponseModel implements ResponseClassInterface
62037 {
62038 public $command;
62039
62040 public static function fromCommand(OperationCommand $command)
62041 {
62042 return new self($command);
62043 }
62044
62045 public function __construct($command)
62046 {
62047 $this->command = $command;
62048 }
62049 }
62050 <?php
62051
62052 namespace Guzzle\Tests\Mock;
62053
62054 use Guzzle\Common\Event;
62055 use Symfony\Component\EventDispatcher\EventSubscriberInterface;
62056
62057 class MockObserver implements \Countable, EventSubscriberInterface
62058 {
62059 public $events = array();
62060
62061 public static function getSubscribedEvents()
62062 {
62063 return array();
62064 }
62065
62066 public function has($eventName)
62067 {
62068 foreach ($this->events as $event) {
62069 if ($event->getName() == $eventName) {
62070 return true;
62071 }
62072 }
62073
62074 return false;
62075 }
62076
62077 public function getLastEvent()
62078 {
62079 return end($this->events);
62080 }
62081
62082 public function count()
62083 {
62084 return count($this->events);
62085 }
62086
62087 public function getGrouped()
62088 {
62089 $events = array();
62090 foreach ($this->events as $event) {
62091 if (!isset($events[$event->getName()])) {
62092 $events[$event->getName()] = array();
62093 }
62094 $events[$event->getName()][] = $event;
62095 }
62096
62097 return $events;
62098 }
62099
62100 public function getData($event, $key, $occurrence = 0)
62101 {
62102 $grouped = $this->getGrouped();
62103 if (isset($grouped[$event])) {
62104 return $grouped[$event][$occurrence][$key];
62105 }
62106
62107 return null;
62108 }
62109
62110 public function update(Event $event)
62111 {
62112 $this->events[] = $event;
62113 }
62114 }
62115 <?php
62116
62117 namespace Guzzle\Tests\Mock;
62118
62119 class ExceptionMock
62120 {
62121 public function __construct()
62122 {
62123 throw new \Exception('Oh no!');
62124 }
62125 }
62126 <?php
62127
62128 namespace Guzzle\Tests\Mock;
62129
62130 class MockSubject extends \Guzzle\Common\Event\AbstractSubject
62131 {
62132 }
62133 <?php
62134
62135 namespace Guzzle\Tests\Mock;
62136
62137 use Guzzle\Plugin\ErrorResponse\ErrorResponseExceptionInterface;
62138 use Guzzle\Service\Command\CommandInterface;
62139 use Guzzle\Http\Message\Response;
62140
62141 class ErrorResponseMock extends \Exception implements ErrorResponseExceptionInterface
62142 {
62143 public $command;
62144 public $response;
62145
62146 public static function fromCommand(CommandInterface $command, Response $response)
62147 {
62148 return new self($command, $response);
62149 }
62150
62151 public function __construct($command, $response)
62152 {
62153 $this->command = $command;
62154 $this->response = $response;
62155 $this->message = 'Error from ' . $response;
62156 }
62157 }
62158 <?php
62159
62160 namespace Guzzle\Tests\Stream;
62161
62162 use Guzzle\Stream\Stream;
62163 use Guzzle\Stream\PhpStreamRequestFactory;
62164 use Guzzle\Http\Client;
62165
62166 /**
62167 * @group server
62168 * @covers \Guzzle\Stream\PhpStreamRequestFactory
62169 */
62170 class PhpStreamRequestFactoryTest extends \Guzzle\Tests\GuzzleTestCase
62171 {
62172 /** @var Client */
62173 protected $client;
62174
62175 /** @var PhpStreamRequestFactory */
62176 protected $factory;
62177
62178 protected function setUp()
62179 {
62180 $this->client = new Client($this->getServer()->getUrl());
62181 $this->factory = new PhpStreamRequestFactory();
62182 }
62183
62184 public function testOpensValidStreamByCreatingContext()
62185 {
62186 $this->getServer()->enqueue("HTTP/1.1 200 OK\r\nContent-Length: 2\r\n\r\nhi");
62187 $request = $this->client->get('/');
62188 $stream = $this->factory->fromRequest($request);
62189 $this->assertEquals('hi', (string) $stream);
62190 $headers = $this->factory->getLastResponseHeaders();
62191 $this->assertContains('HTTP/1.1 200 OK', $headers);
62192 $this->assertContains('Content-Length: 2', $headers);
62193 $this->assertSame($headers, $stream->getCustomData('response_headers'));
62194 $this->assertEquals(2, $stream->getSize());
62195 }
62196
62197 public function testOpensValidStreamByPassingContextAndMerging()
62198 {
62199 $request = $this->client->get('/');
62200 $this->factory = $this->getMockBuilder('Guzzle\Stream\PhpStreamRequestFactory')
62201 ->setMethods(array('createContext', 'createStream'))
62202 ->getMock();
62203 $this->factory->expects($this->never())
62204 ->method('createContext');
62205 $this->factory->expects($this->once())
62206 ->method('createStream')
62207 ->will($this->returnValue(new Stream(fopen('php://temp', 'r'))));
62208
62209 $context = array('http' => array('method' => 'HEAD', 'ignore_errors' => false));
62210 $this->factory->fromRequest($request, stream_context_create($context));
62211 $options = stream_context_get_options($this->readAttribute($this->factory, 'context'));
62212 $this->assertEquals('HEAD', $options['http']['method']);
62213 $this->assertFalse($options['http']['ignore_errors']);
62214 $this->assertEquals('1.0', $options['http']['protocol_version']);
62215 }
62216
62217 public function testAppliesProxySettings()
62218 {
62219 $request = $this->client->get('/');
62220 $request->getCurlOptions()->set(CURLOPT_PROXY, 'tcp://foo.com');
62221 $this->factory = $this->getMockBuilder('Guzzle\Stream\PhpStreamRequestFactory')
62222 ->setMethods(array('createStream'))
62223 ->getMock();
62224 $this->factory->expects($this->once())
62225 ->method('createStream')
62226 ->will($this->returnValue(new Stream(fopen('php://temp', 'r'))));
62227 $this->factory->fromRequest($request);
62228 $options = stream_context_get_options($this->readAttribute($this->factory, 'context'));
62229 $this->assertEquals('tcp://foo.com', $options['http']['proxy']);
62230 }
62231
62232 public function testAddsPostFields()
62233 {
62234 $this->getServer()->flush();
62235 $this->getServer()->enqueue("HTTP/1.1 200 OK\r\nContent-Length: 2\r\n\r\nhi");
62236 $request = $this->client->post('/', array('Foo' => 'Bar'), array('foo' => 'baz bar'));
62237 $stream = $this->factory->fromRequest($request);
62238 $this->assertEquals('hi', (string) $stream);
62239
62240 $headers = $this->factory->getLastResponseHeaders();
62241 $this->assertContains('HTTP/1.1 200 OK', $headers);
62242 $this->assertContains('Content-Length: 2', $headers);
62243 $this->assertSame($headers, $stream->getCustomData('response_headers'));
62244
62245 $received = $this->getServer()->getReceivedRequests();
62246 $this->assertEquals(1, count($received));
62247 $this->assertContains('POST / HTTP/1.0', $received[0]);
62248 $this->assertContains('host: ', $received[0]);
62249 $this->assertContains('user-agent: Guzzle/', $received[0]);
62250 $this->assertContains('foo: Bar', $received[0]);
62251 $this->assertContains('content-length: 13', $received[0]);
62252 $this->assertContains('foo=baz%20bar', $received[0]);
62253 }
62254
62255 public function testAddsBody()
62256 {
62257 $this->getServer()->flush();
62258 $this->getServer()->enqueue("HTTP/1.1 200 OK\r\nContent-Length: 2\r\n\r\nhi");
62259 $request = $this->client->put('/', array('Foo' => 'Bar'), 'Testing...123');
62260 $stream = $this->factory->fromRequest($request);
62261 $this->assertEquals('hi', (string) $stream);
62262
62263 $headers = $this->factory->getLastResponseHeaders();
62264 $this->assertContains('HTTP/1.1 200 OK', $headers);
62265 $this->assertContains('Content-Length: 2', $headers);
62266 $this->assertSame($headers, $stream->getCustomData('response_headers'));
62267
62268 $received = $this->getServer()->getReceivedRequests();
62269 $this->assertEquals(1, count($received));
62270 $this->assertContains('PUT / HTTP/1.0', $received[0]);
62271 $this->assertContains('host: ', $received[0]);
62272 $this->assertContains('user-agent: Guzzle/', $received[0]);
62273 $this->assertContains('foo: Bar', $received[0]);
62274 $this->assertContains('content-length: 13', $received[0]);
62275 $this->assertContains('Testing...123', $received[0]);
62276 }
62277
62278 public function testCanDisableSslValidation()
62279 {
62280 $request = $this->client->get('/');
62281 $request->getCurlOptions()->set(CURLOPT_SSL_VERIFYPEER, false);
62282 $this->factory = $this->getMockBuilder('Guzzle\Stream\PhpStreamRequestFactory')
62283 ->setMethods(array('createStream'))
62284 ->getMock();
62285 $this->factory->expects($this->once())
62286 ->method('createStream')
62287 ->will($this->returnValue(new Stream(fopen('php://temp', 'r'))));
62288 $this->factory->fromRequest($request);
62289 $options = stream_context_get_options($this->readAttribute($this->factory, 'context'));
62290 $this->assertFalse($options['ssl']['verify_peer']);
62291 }
62292
62293 public function testUsesSslValidationByDefault()
62294 {
62295 $request = $this->client->get('/');
62296 $this->factory = $this->getMockBuilder('Guzzle\Stream\PhpStreamRequestFactory')
62297 ->setMethods(array('createStream'))
62298 ->getMock();
62299 $this->factory->expects($this->once())
62300 ->method('createStream')
62301 ->will($this->returnValue(new Stream(fopen('php://temp', 'r'))));
62302 $this->factory->fromRequest($request);
62303 $options = stream_context_get_options($this->readAttribute($this->factory, 'context'));
62304 $this->assertTrue($options['ssl']['verify_peer']);
62305 $this->assertSame($request->getCurlOptions()->get(CURLOPT_CAINFO), $options['ssl']['cafile']);
62306 }
62307
62308 public function testBasicAuthAddsUserAndPassToUrl()
62309 {
62310 $request = $this->client->get('/');
62311 $request->setAuth('Foo', 'Bar');
62312 $this->factory = $this->getMockBuilder('Guzzle\Stream\PhpStreamRequestFactory')
62313 ->setMethods(array('createStream'))
62314 ->getMock();
62315 $this->factory->expects($this->once())
62316 ->method('createStream')
62317 ->will($this->returnValue(new Stream(fopen('php://temp', 'r'))));
62318 $this->factory->fromRequest($request);
62319 $this->assertContains('Foo:Bar@', (string) $this->readAttribute($this->factory, 'url'));
62320 }
62321
62322 public function testCanCreateCustomStreamClass()
62323 {
62324 $this->getServer()->enqueue("HTTP/1.1 200 OK\r\nContent-Length: 2\r\n\r\nhi");
62325 $request = $this->client->get('/');
62326 $stream = $this->factory->fromRequest($request, array(), array('stream_class' => 'Guzzle\Http\EntityBody'));
62327 $this->assertInstanceOf('Guzzle\Http\EntityBody', $stream);
62328 }
62329 }
62330 <?php
62331
62332 namespace Guzzle\Tests\Stream;
62333
62334 use Guzzle\Stream\Stream;
62335
62336 /**
62337 * @group server
62338 * @covers Guzzle\Stream\Stream
62339 */
62340 class StreamTest extends \Guzzle\Tests\GuzzleTestCase
62341 {
62342 /**
62343 * @expectedException \InvalidArgumentException
62344 */
62345 public function testConstructorThrowsExceptionOnInvalidArgument()
62346 {
62347 $stream = new Stream(true);
62348 }
62349
62350 public function testConstructor()
62351 {
62352 $handle = fopen('php://temp', 'r+');
62353 fwrite($handle, 'data');
62354 $stream = new Stream($handle);
62355 $this->assertEquals($handle, $stream->getStream());
62356 $this->assertTrue($stream->isReadable());
62357 $this->assertTrue($stream->isWritable());
62358 $this->assertTrue($stream->isLocal());
62359 $this->assertTrue($stream->isSeekable());
62360 $this->assertEquals('PHP', $stream->getWrapper());
62361 $this->assertEquals('TEMP', $stream->getStreamType());
62362 $this->assertEquals(4, $stream->getSize());
62363 $this->assertEquals('php://temp', $stream->getUri());
62364 $this->assertEquals(array(), $stream->getWrapperData());
62365 $this->assertFalse($stream->isConsumed());
62366 unset($stream);
62367 }
62368
62369 public function testCanModifyStream()
62370 {
62371 $handle1 = fopen('php://temp', 'r+');
62372 $handle2 = fopen('php://temp', 'r+');
62373 $stream = new Stream($handle1);
62374 $this->assertSame($handle1, $stream->getStream());
62375 $stream->setStream($handle2, 10);
62376 $this->assertEquals(10, $stream->getSize());
62377 $this->assertSame($handle2, $stream->getStream());
62378 }
62379
62380 public function testStreamClosesHandleOnDestruct()
62381 {
62382 $handle = fopen('php://temp', 'r');
62383 $stream = new Stream($handle);
62384 unset($stream);
62385 $this->assertFalse(is_resource($handle));
62386 }
62387
62388 public function testConvertsToString()
62389 {
62390 $handle = fopen('php://temp', 'w+');
62391 fwrite($handle, 'data');
62392 $stream = new Stream($handle);
62393 $this->assertEquals('data', (string) $stream);
62394 unset($stream);
62395
62396 $handle = fopen(__DIR__ . '/../TestData/FileBody.txt', 'r');
62397 $stream = new Stream($handle);
62398 $this->assertEquals('', (string) $stream);
62399 unset($stream);
62400 }
62401
62402 public function testConvertsToStringAndRestoresCursorPos()
62403 {
62404 $handle = fopen('php://temp', 'w+');
62405 $stream = new Stream($handle);
62406 $stream->write('foobazbar');
62407 $stream->seek(3);
62408 $this->assertEquals('foobazbar', (string) $stream);
62409 $this->assertEquals(3, $stream->ftell());
62410 }
62411
62412 public function testIsConsumed()
62413 {
62414 $handle = fopen('php://temp', 'w+');
62415 fwrite($handle, 'data');
62416 $stream = new Stream($handle);
62417 $this->assertFalse($stream->isConsumed());
62418 $stream->read(4);
62419 $this->assertTrue($stream->isConsumed());
62420 }
62421
62422 public function testAllowsSettingManualSize()
62423 {
62424 $handle = fopen('php://temp', 'w+');
62425 fwrite($handle, 'data');
62426 $stream = new Stream($handle);
62427 $stream->setSize(10);
62428 $this->assertEquals(10, $stream->getSize());
62429 unset($stream);
62430 }
62431
62432 public function testWrapsStream()
62433 {
62434 $handle = fopen('php://temp', 'w+');
62435 fwrite($handle, 'data');
62436 $stream = new Stream($handle);
62437 $this->assertTrue($stream->isSeekable());
62438 $this->assertTrue($stream->isReadable());
62439 $this->assertTrue($stream->seek(0));
62440 $this->assertEquals('da', $stream->read(2));
62441 $this->assertEquals('ta', $stream->read(2));
62442 $this->assertTrue($stream->seek(0));
62443 $this->assertEquals('data', $stream->read(4));
62444 $stream->write('_appended');
62445 $stream->seek(0);
62446 $this->assertEquals('data_appended', $stream->read(13));
62447 }
62448
62449 public function testGetSize()
62450 {
62451 $size = filesize(__DIR__ . '/../../../bootstrap.php');
62452 $handle = fopen(__DIR__ . '/../../../bootstrap.php', 'r');
62453 $stream = new Stream($handle);
62454 $this->assertEquals($handle, $stream->getStream());
62455 $this->assertEquals($size, $stream->getSize());
62456 $this->assertEquals($size, $stream->getSize());
62457 unset($stream);
62458
62459 // Make sure that false is returned when the size cannot be determined
62460 $this->getServer()->enqueue("HTTP/1.1 200 OK\r\nContent-Length: 0\r\n\r\n");
62461 $handle = fopen('http://localhost:' . $this->getServer()->getPort(), 'r');
62462 $stream = new Stream($handle);
62463 $this->assertEquals(false, $stream->getSize());
62464 unset($stream);
62465 }
62466
62467 public function testEnsuresSizeIsConsistent()
62468 {
62469 $h = fopen('php://temp', 'r+');
62470 fwrite($h, 'foo');
62471 $stream = new Stream($h);
62472 $this->assertEquals(3, $stream->getSize());
62473 $stream->write('test');
62474 $this->assertEquals(7, $stream->getSize());
62475 fclose($h);
62476 }
62477
62478 public function testAbstractsMetaData()
62479 {
62480 $handle = fopen(__DIR__ . '/../../../bootstrap.php', 'r');
62481 $stream = new Stream($handle);
62482 $this->assertEquals('plainfile', $stream->getMetaData('wrapper_type'));
62483 $this->assertEquals(null, $stream->getMetaData('wrapper_data'));
62484 $this->assertInternalType('array', $stream->getMetaData());
62485 }
62486
62487 public function testDoesNotAttemptToWriteToReadonlyStream()
62488 {
62489 $handle = fopen(__DIR__ . '/../../../bootstrap.php', 'r');
62490 $stream = new Stream($handle);
62491 $this->assertEquals(0, $stream->write('foo'));
62492 }
62493
62494 public function testProvidesStreamPosition()
62495 {
62496 $handle = fopen(__DIR__ . '/../../../bootstrap.php', 'r');
62497 $stream = new Stream($handle);
62498 $stream->read(2);
62499 $this->assertSame(ftell($handle), $stream->ftell());
62500 $this->assertEquals(2, $stream->ftell());
62501 }
62502
62503 public function testRewindIsSeekZero()
62504 {
62505 $stream = new Stream(fopen('php://temp', 'w+'));
62506 $stream->write('foobazbar');
62507 $this->assertTrue($stream->rewind());
62508 $this->assertEquals('foobazbar', $stream->read(9));
62509 }
62510
62511 public function testCanDetachStream()
62512 {
62513 $r = fopen('php://temp', 'w+');
62514 $stream = new Stream($r);
62515 $stream->detachStream();
62516 $this->assertNull($stream->getStream());
62517 }
62518 }
62519 <?php
62520
62521 namespace Guzzle\Tests\Inflection;
62522
62523 use Guzzle\Inflection\PreComputedInflector;
62524
62525 /**
62526 * @covers Guzzle\Inflection\PreComputedInflector
62527 */
62528 class PreComputedInflectorTest extends \Guzzle\Tests\GuzzleTestCase
62529 {
62530 public function testUsesPreComputedHash()
62531 {
62532 $mock = $this->getMock('Guzzle\Inflection\Inflector', array('snake', 'camel'));
62533 $mock->expects($this->once())->method('snake')->with('Test')->will($this->returnValue('test'));
62534 $mock->expects($this->once())->method('camel')->with('Test')->will($this->returnValue('Test'));
62535 $inflector = new PreComputedInflector($mock, array('FooBar' => 'foo_bar'), array('foo_bar' => 'FooBar'));
62536 $this->assertEquals('FooBar', $inflector->camel('foo_bar'));
62537 $this->assertEquals('foo_bar', $inflector->snake('FooBar'));
62538 $this->assertEquals('Test', $inflector->camel('Test'));
62539 $this->assertEquals('test', $inflector->snake('Test'));
62540 }
62541
62542 public function testMirrorsPrecomputedValues()
62543 {
62544 $mock = $this->getMock('Guzzle\Inflection\Inflector', array('snake', 'camel'));
62545 $mock->expects($this->never())->method('snake');
62546 $mock->expects($this->never())->method('camel');
62547 $inflector = new PreComputedInflector($mock, array('Zeep' => 'zeep'), array(), true);
62548 $this->assertEquals('Zeep', $inflector->camel('zeep'));
62549 $this->assertEquals('zeep', $inflector->snake('Zeep'));
62550 }
62551
62552 public function testMirrorsPrecomputedValuesByMerging()
62553 {
62554 $mock = $this->getMock('Guzzle\Inflection\Inflector', array('snake', 'camel'));
62555 $mock->expects($this->never())->method('snake');
62556 $mock->expects($this->never())->method('camel');
62557 $inflector = new PreComputedInflector($mock, array('Zeep' => 'zeep'), array('foo' => 'Foo'), true);
62558 $this->assertEquals('Zeep', $inflector->camel('zeep'));
62559 $this->assertEquals('zeep', $inflector->snake('Zeep'));
62560 $this->assertEquals('Foo', $inflector->camel('foo'));
62561 $this->assertEquals('foo', $inflector->snake('Foo'));
62562 }
62563 }
62564 <?php
62565
62566 namespace Guzzle\Tests\Inflection;
62567
62568 use Guzzle\Inflection\MemoizingInflector;
62569 use Guzzle\Inflection\Inflector;
62570
62571 /**
62572 * @covers Guzzle\Inflection\MemoizingInflector
62573 */
62574 class MemoizingInflectorTest extends \Guzzle\Tests\GuzzleTestCase
62575 {
62576 public function testUsesCache()
62577 {
62578 $mock = $this->getMock('Guzzle\Inflection\Inflector', array('snake', 'camel'));
62579 $mock->expects($this->once())->method('snake')->will($this->returnValue('foo_bar'));
62580 $mock->expects($this->once())->method('camel')->will($this->returnValue('FooBar'));
62581
62582 $inflector = new MemoizingInflector($mock);
62583 $this->assertEquals('foo_bar', $inflector->snake('FooBar'));
62584 $this->assertEquals('foo_bar', $inflector->snake('FooBar'));
62585 $this->assertEquals('FooBar', $inflector->camel('foo_bar'));
62586 $this->assertEquals('FooBar', $inflector->camel('foo_bar'));
62587 }
62588
62589 public function testProtectsAgainstCacheOverflow()
62590 {
62591 $inflector = new MemoizingInflector(new Inflector(), 10);
62592 for ($i = 1; $i < 11; $i++) {
62593 $inflector->camel('foo_' . $i);
62594 $inflector->snake('Foo' . $i);
62595 }
62596
62597 $cache = $this->readAttribute($inflector, 'cache');
62598 $this->assertEquals(10, count($cache['snake']));
62599 $this->assertEquals(10, count($cache['camel']));
62600
62601 $inflector->camel('baz!');
62602 $inflector->snake('baz!');
62603
62604 // Now ensure that 20% of the cache was removed (2), then the item was added
62605 $cache = $this->readAttribute($inflector, 'cache');
62606 $this->assertEquals(9, count($cache['snake']));
62607 $this->assertEquals(9, count($cache['camel']));
62608 }
62609 }
62610 <?php
62611
62612 namespace Guzzle\Tests\Inflection;
62613
62614 use Guzzle\Inflection\Inflector;
62615
62616 /**
62617 * @covers Guzzle\Inflection\Inflector
62618 */
62619 class InflectorTest extends \Guzzle\Tests\GuzzleTestCase
62620 {
62621 public function testReturnsDefaultInstance()
62622 {
62623 $this->assertSame(Inflector::getDefault(), Inflector::getDefault());
62624 }
62625
62626 public function testSnake()
62627 {
62628 $this->assertEquals('camel_case', Inflector::getDefault()->snake('camelCase'));
62629 $this->assertEquals('camel_case', Inflector::getDefault()->snake('CamelCase'));
62630 $this->assertEquals('camel_case_words', Inflector::getDefault()->snake('CamelCaseWords'));
62631 $this->assertEquals('camel_case_words', Inflector::getDefault()->snake('CamelCase_words'));
62632 $this->assertEquals('test', Inflector::getDefault()->snake('test'));
62633 $this->assertEquals('test', Inflector::getDefault()->snake('test'));
62634 $this->assertEquals('expect100_continue', Inflector::getDefault()->snake('Expect100Continue'));
62635 }
62636
62637 public function testCamel()
62638 {
62639 $this->assertEquals('CamelCase', Inflector::getDefault()->camel('camel_case'));
62640 $this->assertEquals('CamelCaseWords', Inflector::getDefault()->camel('camel_case_words'));
62641 $this->assertEquals('Test', Inflector::getDefault()->camel('test'));
62642 $this->assertEquals('Expect100Continue', ucfirst(Inflector::getDefault()->camel('expect100_continue')));
62643 // Get from cache
62644 $this->assertEquals('Test', Inflector::getDefault()->camel('test', false));
62645 }
62646 }
62647 CHANGELOG
62648 =========
62649
62650 3.7.3 (2013-09-08)
62651 ------------------
62652
62653 * Added the ability to get the exception associated with a request/command when using `MultiTransferException` and
62654 `CommandTransferException`.
62655 * Setting `additionalParameters` of a response to false is now honored when parsing responses with a service description
62656 * Schemas are only injected into response models when explicitly configured.
62657 * No longer guessing Content-Type based on the path of a request. Content-Type is now only guessed based on the path of
62658 an EntityBody.
62659 * Bug fix: ChunkedIterator can now properly chunk a \Traversable as well as an \Iterator.
62660 * Bug fix: FilterIterator now relies on `\Iterator` instead of `\Traversable`.
62661 * Bug fix: Gracefully handling malformed responses in RequestMediator::writeResponseBody()
62662 * Bug fix: Replaced call to canCache with canCacheRequest in the CallbackCanCacheStrategy of the CachePlugin
62663 * Bug fix: Visiting XML attributes first before visting XML children when serializing requests
62664 * Bug fix: Properly parsing headers that contain commas contained in quotes
62665 * Bug fix: mimetype guessing based on a filename is now case-insensitive
62666
62667 3.7.2 (2013-08-02)
62668 ------------------
62669
62670 * Bug fix: Properly URL encoding paths when using the PHP-only version of the UriTemplate expander
62671 See https://github.com/guzzle/guzzle/issues/371
62672 * Bug fix: Cookie domains are now matched correctly according to RFC 6265
62673 See https://github.com/guzzle/guzzle/issues/377
62674 * Bug fix: GET parameters are now used when calculating an OAuth signature
62675 * Bug fix: Fixed an issue with cache revalidation where the If-None-Match header was being double quoted
62676 * `Guzzle\Common\AbstractHasDispatcher::dispatch()` now returns the event that was dispatched
62677 * `Guzzle\Http\QueryString::factory()` now guesses the most appropriate query aggregator to used based on the input.
62678 See https://github.com/guzzle/guzzle/issues/379
62679 * Added a way to add custom domain objects to service description parsing using the `operation.parse_class` event. See
62680 https://github.com/guzzle/guzzle/pull/380
62681 * cURL multi cleanup and optimizations
62682
62683 3.7.1 (2013-07-05)
62684 ------------------
62685
62686 * Bug fix: Setting default options on a client now works
62687 * Bug fix: Setting options on HEAD requests now works. See #352
62688 * Bug fix: Moving stream factory before send event to before building the stream. See #353
62689 * Bug fix: Cookies no longer match on IP addresses per RFC 6265
62690 * Bug fix: Correctly parsing header parameters that are in `<>` and quotes
62691 * Added `cert` and `ssl_key` as request options
62692 * `Host` header can now diverge from the host part of a URL if the header is set manually
62693 * `Guzzle\Service\Command\LocationVisitor\Request\XmlVisitor` was rewritten to change from using SimpleXML to XMLWriter
62694 * OAuth parameters are only added via the plugin if they aren't already set
62695 * Exceptions are now thrown when a URL cannot be parsed
62696 * Returning `false` if `Guzzle\Http\EntityBody::getContentMd5()` fails
62697 * Not setting a `Content-MD5` on a command if calculating the Content-MD5 fails via the CommandContentMd5Plugin
62698
62699 3.7.0 (2013-06-10)
62700 ------------------
62701
62702 * See UPGRADING.md for more information on how to upgrade.
62703 * Requests now support the ability to specify an array of $options when creating a request to more easily modify a
62704 request. You can pass a 'request.options' configuration setting to a client to apply default request options to
62705 every request created by a client (e.g. default query string variables, headers, curl options, etc).
62706 * Added a static facade class that allows you to use Guzzle with static methods and mount the class to `\Guzzle`.
62707 See `Guzzle\Http\StaticClient::mount`.
62708 * Added `command.request_options` to `Guzzle\Service\Command\AbstractCommand` to pass request options to requests
62709 created by a command (e.g. custom headers, query string variables, timeout settings, etc).
62710 * Stream size in `Guzzle\Stream\PhpStreamRequestFactory` will now be set if Content-Length is returned in the
62711 headers of a response
62712 * Added `Guzzle\Common\Collection::setPath($path, $value)` to set a value into an array using a nested key
62713 (e.g. `$collection->setPath('foo/baz/bar', 'test'); echo $collection['foo']['bar']['bar'];`)
62714 * ServiceBuilders now support storing and retrieving arbitrary data
62715 * CachePlugin can now purge all resources for a given URI
62716 * CachePlugin can automatically purge matching cached items when a non-idempotent request is sent to a resource
62717 * CachePlugin now uses the Vary header to determine if a resource is a cache hit
62718 * `Guzzle\Http\Message\Response` now implements `\Serializable`
62719 * Added `Guzzle\Cache\CacheAdapterFactory::fromCache()` to more easily create cache adapters
62720 * `Guzzle\Service\ClientInterface::execute()` now accepts an array, single command, or Traversable
62721 * Fixed a bug in `Guzzle\Http\Message\Header\Link::addLink()`
62722 * Better handling of calculating the size of a stream in `Guzzle\Stream\Stream` using fstat() and caching the size
62723 * `Guzzle\Common\Exception\ExceptionCollection` now creates a more readable exception message
62724 * Fixing BC break: Added back the MonologLogAdapter implementation rather than extending from PsrLog so that older
62725 Symfony users can still use the old version of Monolog.
62726 * Fixing BC break: Added the implementation back in for `Guzzle\Http\Message\AbstractMessage::getTokenizedHeader()`.
62727 Now triggering an E_USER_DEPRECATED warning when used. Use `$message->getHeader()->parseParams()`.
62728 * Several performance improvements to `Guzzle\Common\Collection`
62729 * Added an `$options` argument to the end of the following methods of `Guzzle\Http\ClientInterface`:
62730 createRequest, head, delete, put, patch, post, options, prepareRequest
62731 * Added an `$options` argument to the end of `Guzzle\Http\Message\Request\RequestFactoryInterface::createRequest()`
62732 * Added an `applyOptions()` method to `Guzzle\Http\Message\Request\RequestFactoryInterface`
62733 * Changed `Guzzle\Http\ClientInterface::get($uri = null, $headers = null, $body = null)` to
62734 `Guzzle\Http\ClientInterface::get($uri = null, $headers = null, $options = array())`. You can still pass in a
62735 resource, string, or EntityBody into the $options parameter to specify the download location of the response.
62736 * Changed `Guzzle\Common\Collection::__construct($data)` to no longer accepts a null value for `$data` but a
62737 default `array()`
62738 * Added `Guzzle\Stream\StreamInterface::isRepeatable`
62739 * Removed `Guzzle\Http\ClientInterface::setDefaultHeaders(). Use
62740 $client->getConfig()->setPath('request.options/headers/{header_name}', 'value')`. or
62741 $client->getConfig()->setPath('request.options/headers', array('header_name' => 'value'))`.
62742 * Removed `Guzzle\Http\ClientInterface::getDefaultHeaders(). Use $client->getConfig()->getPath('request.options/headers')`.
62743 * Removed `Guzzle\Http\ClientInterface::expandTemplate()`
62744 * Removed `Guzzle\Http\ClientInterface::setRequestFactory()`
62745 * Removed `Guzzle\Http\ClientInterface::getCurlMulti()`
62746 * Removed `Guzzle\Http\Message\RequestInterface::canCache`
62747 * Removed `Guzzle\Http\Message\RequestInterface::setIsRedirect`
62748 * Removed `Guzzle\Http\Message\RequestInterface::isRedirect`
62749 * Made `Guzzle\Http\Client::expandTemplate` and `getUriTemplate` protected methods.
62750 * You can now enable E_USER_DEPRECATED warnings to see if you are using a deprecated method by setting
62751 `Guzzle\Common\Version::$emitWarnings` to true.
62752 * Marked `Guzzle\Http\Message\Request::isResponseBodyRepeatable()` as deprecated. Use
62753 `$request->getResponseBody()->isRepeatable()` instead.
62754 * Marked `Guzzle\Http\Message\Request::canCache()` as deprecated. Use
62755 `Guzzle\Plugin\Cache\DefaultCanCacheStrategy->canCacheRequest()` instead.
62756 * Marked `Guzzle\Http\Message\Request::canCache()` as deprecated. Use
62757 `Guzzle\Plugin\Cache\DefaultCanCacheStrategy->canCacheRequest()` instead.
62758 * Marked `Guzzle\Http\Message\Request::setIsRedirect()` as deprecated. Use the HistoryPlugin instead.
62759 * Marked `Guzzle\Http\Message\Request::isRedirect()` as deprecated. Use the HistoryPlugin instead.
62760 * Marked `Guzzle\Cache\CacheAdapterFactory::factory()` as deprecated
62761 * Marked 'command.headers', 'command.response_body' and 'command.on_complete' as deprecated for AbstractCommand.
62762 These will work through Guzzle 4.0
62763 * Marked 'request.params' for `Guzzle\Http\Client` as deprecated. Use [request.options][params].
62764 * Marked `Guzzle\Service\Client::enableMagicMethods()` as deprecated. Magic methods can no longer be disabled on a Guzzle\Service\Client.
62765 * Marked `Guzzle\Service\Client::getDefaultHeaders()` as deprecated. Use $client->getConfig()->getPath('request.options/headers')`.
62766 * Marked `Guzzle\Service\Client::setDefaultHeaders()` as deprecated. Use $client->getConfig()->setPath('request.options/headers/{header_name}', 'value')`.
62767 * Marked `Guzzle\Parser\Url\UrlParser` as deprecated. Just use PHP's `parse_url()` and percent encode your UTF-8.
62768 * Marked `Guzzle\Common\Collection::inject()` as deprecated.
62769 * Marked `Guzzle\Plugin\CurlAuth\CurlAuthPlugin` as deprecated. Use `$client->getConfig()->setPath('request.options/auth', array('user', 'pass', 'Basic|Digest');`
62770 * CacheKeyProviderInterface and DefaultCacheKeyProvider are no longer used. All of this logic is handled in a
62771 CacheStorageInterface. These two objects and interface will be removed in a future version.
62772 * Always setting X-cache headers on cached responses
62773 * Default cache TTLs are now handled by the CacheStorageInterface of a CachePlugin
62774 * `CacheStorageInterface::cache($key, Response $response, $ttl = null)` has changed to `cache(RequestInterface
62775 $request, Response $response);`
62776 * `CacheStorageInterface::fetch($key)` has changed to `fetch(RequestInterface $request);`
62777 * `CacheStorageInterface::delete($key)` has changed to `delete(RequestInterface $request);`
62778 * Added `CacheStorageInterface::purge($url)`
62779 * `DefaultRevalidation::__construct(CacheKeyProviderInterface $cacheKey, CacheStorageInterface $cache, CachePlugin
62780 $plugin)` has changed to `DefaultRevalidation::__construct(CacheStorageInterface $cache,
62781 CanCacheStrategyInterface $canCache = null)`
62782 * Added `RevalidationInterface::shouldRevalidate(RequestInterface $request, Response $response)`
62783
62784 3.6.0 (2013-05-29)
62785 ------------------
62786
62787 * ServiceDescription now implements ToArrayInterface
62788 * Added command.hidden_params to blacklist certain headers from being treated as additionalParameters
62789 * Guzzle can now correctly parse incomplete URLs
62790 * Mixed casing of headers are now forced to be a single consistent casing across all values for that header.
62791 * Messages internally use a HeaderCollection object to delegate handling case-insensitive header resolution
62792 * Removed the whole changedHeader() function system of messages because all header changes now go through addHeader().
62793 * Specific header implementations can be created for complex headers. When a message creates a header, it uses a
62794 HeaderFactory which can map specific headers to specific header classes. There is now a Link header and
62795 CacheControl header implementation.
62796 * Removed from interface: Guzzle\Http\ClientInterface::setUriTemplate
62797 * Removed from interface: Guzzle\Http\ClientInterface::setCurlMulti()
62798 * Removed Guzzle\Http\Message\Request::receivedRequestHeader() and implemented this functionality in
62799 Guzzle\Http\Curl\RequestMediator
62800 * Removed the optional $asString parameter from MessageInterface::getHeader(). Just cast the header to a string.
62801 * Removed the optional $tryChunkedTransfer option from Guzzle\Http\Message\EntityEnclosingRequestInterface
62802 * Removed the $asObjects argument from Guzzle\Http\Message\MessageInterface::getHeaders()
62803 * Removed Guzzle\Parser\ParserRegister::get(). Use getParser()
62804 * Removed Guzzle\Parser\ParserRegister::set(). Use registerParser().
62805 * All response header helper functions return a string rather than mixing Header objects and strings inconsistently
62806 * Removed cURL blacklist support. This is no longer necessary now that Expect, Accept, etc are managed by Guzzle
62807 directly via interfaces
62808 * Removed the injecting of a request object onto a response object. The methods to get and set a request still exist
62809 but are a no-op until removed.
62810 * Most classes that used to require a ``Guzzle\Service\Command\CommandInterface` typehint now request a
62811 `Guzzle\Service\Command\ArrayCommandInterface`.
62812 * Added `Guzzle\Http\Message\RequestInterface::startResponse()` to the RequestInterface to handle injecting a response
62813 on a request while the request is still being transferred
62814 * The ability to case-insensitively search for header values
62815 * Guzzle\Http\Message\Header::hasExactHeader
62816 * Guzzle\Http\Message\Header::raw. Use getAll()
62817 * Deprecated cache control specific methods on Guzzle\Http\Message\AbstractMessage. Use the CacheControl header object
62818 instead.
62819 * `Guzzle\Service\Command\CommandInterface` now extends from ToArrayInterface and ArrayAccess
62820 * Added the ability to cast Model objects to a string to view debug information.
62821
62822 3.5.0 (2013-05-13)
62823 ------------------
62824
62825 * Bug: Fixed a regression so that request responses are parsed only once per oncomplete event rather than multiple times
62826 * Bug: Better cleanup of one-time events accross the board (when an event is meant to fire once, it will now remove
62827 itself from the EventDispatcher)
62828 * Bug: `Guzzle\Log\MessageFormatter` now properly writes "total_time" and "connect_time" values
62829 * Bug: Cloning an EntityEnclosingRequest now clones the EntityBody too
62830 * Bug: Fixed an undefined index error when parsing nested JSON responses with a sentAs parameter that reference a
62831 non-existent key
62832 * Bug: All __call() method arguments are now required (helps with mocking frameworks)
62833 * Deprecating Response::getRequest() and now using a shallow clone of a request object to remove a circular reference
62834 to help with refcount based garbage collection of resources created by sending a request
62835 * Deprecating ZF1 cache and log adapters. These will be removed in the next major version.
62836 * Deprecating `Response::getPreviousResponse()` (method signature still exists, but it'sdeprecated). Use the
62837 HistoryPlugin for a history.
62838 * Added a `responseBody` alias for the `response_body` location
62839 * Refactored internals to no longer rely on Response::getRequest()
62840 * HistoryPlugin can now be cast to a string
62841 * HistoryPlugin now logs transactions rather than requests and responses to more accurately keep track of the requests
62842 and responses that are sent over the wire
62843 * Added `getEffectiveUrl()` and `getRedirectCount()` to Response objects
62844
62845 3.4.3 (2013-04-30)
62846 ------------------
62847
62848 * Bug fix: Fixing bug introduced in 3.4.2 where redirect responses are duplicated on the final redirected response
62849 * Added a check to re-extract the temp cacert bundle from the phar before sending each request
62850
62851 3.4.2 (2013-04-29)
62852 ------------------
62853
62854 * Bug fix: Stream objects now work correctly with "a" and "a+" modes
62855 * Bug fix: Removing `Transfer-Encoding: chunked` header when a Content-Length is present
62856 * Bug fix: AsyncPlugin no longer forces HEAD requests
62857 * Bug fix: DateTime timezones are now properly handled when using the service description schema formatter
62858 * Bug fix: CachePlugin now properly handles stale-if-error directives when a request to the origin server fails
62859 * Setting a response on a request will write to the custom request body from the response body if one is specified
62860 * LogPlugin now writes to php://output when STDERR is undefined
62861 * Added the ability to set multiple POST files for the same key in a single call
62862 * application/x-www-form-urlencoded POSTs now use the utf-8 charset by default
62863 * Added the ability to queue CurlExceptions to the MockPlugin
62864 * Cleaned up how manual responses are queued on requests (removed "queued_response" and now using request.before_send)
62865 * Configuration loading now allows remote files
62866
62867 3.4.1 (2013-04-16)
62868 ------------------
62869
62870 * Large refactoring to how CurlMulti handles work. There is now a proxy that sits in front of a pool of CurlMulti
62871 handles. This greatly simplifies the implementation, fixes a couple bugs, and provides a small performance boost.
62872 * Exceptions are now properly grouped when sending requests in parallel
62873 * Redirects are now properly aggregated when a multi transaction fails
62874 * Redirects now set the response on the original object even in the event of a failure
62875 * Bug fix: Model names are now properly set even when using $refs
62876 * Added support for PHP 5.5's CurlFile to prevent warnings with the deprecated @ syntax
62877 * Added support for oauth_callback in OAuth signatures
62878 * Added support for oauth_verifier in OAuth signatures
62879 * Added support to attempt to retrieve a command first literally, then ucfirst, the with inflection
62880
62881 3.4.0 (2013-04-11)
62882 ------------------
62883
62884 * Bug fix: URLs are now resolved correctly based on http://tools.ietf.org/html/rfc3986#section-5.2. #289
62885 * Bug fix: Absolute URLs with a path in a service description will now properly override the base URL. #289
62886 * Bug fix: Parsing a query string with a single PHP array value will now result in an array. #263
62887 * Bug fix: Better normalization of the User-Agent header to prevent duplicate headers. #264.
62888 * Bug fix: Added `number` type to service descriptions.
62889 * Bug fix: empty parameters are removed from an OAuth signature
62890 * Bug fix: Revalidating a cache entry prefers the Last-Modified over the Date header
62891 * Bug fix: Fixed "array to string" error when validating a union of types in a service description
62892 * Bug fix: Removed code that attempted to determine the size of a stream when data is written to the stream
62893 * Bug fix: Not including an `oauth_token` if the value is null in the OauthPlugin.
62894 * Bug fix: Now correctly aggregating successful requests and failed requests in CurlMulti when a redirect occurs.
62895 * The new default CURLOPT_TIMEOUT setting has been increased to 150 seconds so that Guzzle works on poor connections.
62896 * Added a feature to EntityEnclosingRequest::setBody() that will automatically set the Content-Type of the request if
62897 the Content-Type can be determined based on the entity body or the path of the request.
62898 * Added the ability to overwrite configuration settings in a client when grabbing a throwaway client from a builder.
62899 * Added support for a PSR-3 LogAdapter.
62900 * Added a `command.after_prepare` event
62901 * Added `oauth_callback` parameter to the OauthPlugin
62902 * Added the ability to create a custom stream class when using a stream factory
62903 * Added a CachingEntityBody decorator
62904 * Added support for `additionalParameters` in service descriptions to define how custom parameters are serialized.
62905 * The bundled SSL certificate is now provided in the phar file and extracted when running Guzzle from a phar.
62906 * You can now send any EntityEnclosingRequest with POST fields or POST files and cURL will handle creating bodies
62907 * POST requests using a custom entity body are now treated exactly like PUT requests but with a custom cURL method. This
62908 means that the redirect behavior of POST requests with custom bodies will not be the same as POST requests that use
62909 POST fields or files (the latter is only used when emulating a form POST in the browser).
62910 * Lots of cleanup to CurlHandle::factory and RequestFactory::createRequest
62911
62912 3.3.1 (2013-03-10)
62913 ------------------
62914
62915 * Added the ability to create PHP streaming responses from HTTP requests
62916 * Bug fix: Running any filters when parsing response headers with service descriptions
62917 * Bug fix: OauthPlugin fixes to allow for multi-dimensional array signing, and sorting parameters before signing
62918 * Bug fix: Removed the adding of default empty arrays and false Booleans to responses in order to be consistent across
62919 response location visitors.
62920 * Bug fix: Removed the possibility of creating configuration files with circular dependencies
62921 * RequestFactory::create() now uses the key of a POST file when setting the POST file name
62922 * Added xmlAllowEmpty to serialize an XML body even if no XML specific parameters are set
62923
62924 3.3.0 (2013-03-03)
62925 ------------------
62926
62927 * A large number of performance optimizations have been made
62928 * Bug fix: Added 'wb' as a valid write mode for streams
62929 * Bug fix: `Guzzle\Http\Message\Response::json()` now allows scalar values to be returned
62930 * Bug fix: Fixed bug in `Guzzle\Http\Message\Response` where wrapping quotes were stripped from `getEtag()`
62931 * BC: Removed `Guzzle\Http\Utils` class
62932 * BC: Setting a service description on a client will no longer modify the client's command factories.
62933 * BC: Emitting IO events from a RequestMediator is now a parameter that must be set in a request's curl options using
62934 the 'emit_io' key. This was previously set under a request's parameters using 'curl.emit_io'
62935 * BC: `Guzzle\Stream\Stream::getWrapper()` and `Guzzle\Stream\Stream::getSteamType()` are no longer converted to
62936 lowercase
62937 * Operation parameter objects are now lazy loaded internally
62938 * Added ErrorResponsePlugin that can throw errors for responses defined in service description operations' errorResponses
62939 * Added support for instantiating responseType=class responseClass classes. Classes must implement
62940 `Guzzle\Service\Command\ResponseClassInterface`
62941 * Added support for additionalProperties for top-level parameters in responseType=model responseClasses. These
62942 additional properties also support locations and can be used to parse JSON responses where the outermost part of the
62943 JSON is an array
62944 * Added support for nested renaming of JSON models (rename sentAs to name)
62945 * CachePlugin
62946 * Added support for stale-if-error so that the CachePlugin can now serve stale content from the cache on error
62947 * Debug headers can now added to cached response in the CachePlugin
62948
62949 3.2.0 (2013-02-14)
62950 ------------------
62951
62952 * CurlMulti is no longer reused globally. A new multi object is created per-client. This helps to isolate clients.
62953 * URLs with no path no longer contain a "/" by default
62954 * Guzzle\Http\QueryString does no longer manages the leading "?". This is now handled in Guzzle\Http\Url.
62955 * BadResponseException no longer includes the full request and response message
62956 * Adding setData() to Guzzle\Service\Description\ServiceDescriptionInterface
62957 * Adding getResponseBody() to Guzzle\Http\Message\RequestInterface
62958 * Various updates to classes to use ServiceDescriptionInterface type hints rather than ServiceDescription
62959 * Header values can now be normalized into distinct values when multiple headers are combined with a comma separated list
62960 * xmlEncoding can now be customized for the XML declaration of a XML service description operation
62961 * Guzzle\Http\QueryString now uses Guzzle\Http\QueryAggregator\QueryAggregatorInterface objects to add custom value
62962 aggregation and no longer uses callbacks
62963 * The URL encoding implementation of Guzzle\Http\QueryString can now be customized
62964 * Bug fix: Filters were not always invoked for array service description parameters
62965 * Bug fix: Redirects now use a target response body rather than a temporary response body
62966 * Bug fix: The default exponential backoff BackoffPlugin was not giving when the request threshold was exceeded
62967 * Bug fix: Guzzle now takes the first found value when grabbing Cache-Control directives
62968
62969 3.1.2 (2013-01-27)
62970 ------------------
62971
62972 * Refactored how operation responses are parsed. Visitors now include a before() method responsible for parsing the
62973 response body. For example, the XmlVisitor now parses the XML response into an array in the before() method.
62974 * Fixed an issue where cURL would not automatically decompress responses when the Accept-Encoding header was sent
62975 * CURLOPT_SSL_VERIFYHOST is never set to 1 because it is deprecated (see 5e0ff2ef20f839e19d1eeb298f90ba3598784444)
62976 * Fixed a bug where redirect responses were not chained correctly using getPreviousResponse()
62977 * Setting default headers on a client after setting the user-agent will not erase the user-agent setting
62978
62979 3.1.1 (2013-01-20)
62980 ------------------
62981
62982 * Adding wildcard support to Guzzle\Common\Collection::getPath()
62983 * Adding alias support to ServiceBuilder configs
62984 * Adding Guzzle\Service\Resource\CompositeResourceIteratorFactory and cleaning up factory interface
62985
62986 3.1.0 (2013-01-12)
62987 ------------------
62988
62989 * BC: CurlException now extends from RequestException rather than BadResponseException
62990 * BC: Renamed Guzzle\Plugin\Cache\CanCacheStrategyInterface::canCache() to canCacheRequest() and added CanCacheResponse()
62991 * Added getData to ServiceDescriptionInterface
62992 * Added context array to RequestInterface::setState()
62993 * Bug: Removing hard dependency on the BackoffPlugin from Guzzle\Http
62994 * Bug: Adding required content-type when JSON request visitor adds JSON to a command
62995 * Bug: Fixing the serialization of a service description with custom data
62996 * Made it easier to deal with exceptions thrown when transferring commands or requests in parallel by providing
62997 an array of successful and failed responses
62998 * Moved getPath from Guzzle\Service\Resource\Model to Guzzle\Common\Collection
62999 * Added Guzzle\Http\IoEmittingEntityBody
63000 * Moved command filtration from validators to location visitors
63001 * Added `extends` attributes to service description parameters
63002 * Added getModels to ServiceDescriptionInterface
63003
63004 3.0.7 (2012-12-19)
63005 ------------------
63006
63007 * Fixing phar detection when forcing a cacert to system if null or true
63008 * Allowing filename to be passed to `Guzzle\Http\Message\Request::setResponseBody()`
63009 * Cleaning up `Guzzle\Common\Collection::inject` method
63010 * Adding a response_body location to service descriptions
63011
63012 3.0.6 (2012-12-09)
63013 ------------------
63014
63015 * CurlMulti performance improvements
63016 * Adding setErrorResponses() to Operation
63017 * composer.json tweaks
63018
63019 3.0.5 (2012-11-18)
63020 ------------------
63021
63022 * Bug: Fixing an infinite recursion bug caused from revalidating with the CachePlugin
63023 * Bug: Response body can now be a string containing "0"
63024 * Bug: Using Guzzle inside of a phar uses system by default but now allows for a custom cacert
63025 * Bug: QueryString::fromString now properly parses query string parameters that contain equal signs
63026 * Added support for XML attributes in service description responses
63027 * DefaultRequestSerializer now supports array URI parameter values for URI template expansion
63028 * Added better mimetype guessing to requests and post files
63029
63030 3.0.4 (2012-11-11)
63031 ------------------
63032
63033 * Bug: Fixed a bug when adding multiple cookies to a request to use the correct glue value
63034 * Bug: Cookies can now be added that have a name, domain, or value set to "0"
63035 * Bug: Using the system cacert bundle when using the Phar
63036 * Added json and xml methods to Response to make it easier to parse JSON and XML response data into data structures
63037 * Enhanced cookie jar de-duplication
63038 * Added the ability to enable strict cookie jars that throw exceptions when invalid cookies are added
63039 * Added setStream to StreamInterface to actually make it possible to implement custom rewind behavior for entity bodies
63040 * Added the ability to create any sort of hash for a stream rather than just an MD5 hash
63041
63042 3.0.3 (2012-11-04)
63043 ------------------
63044
63045 * Implementing redirects in PHP rather than cURL
63046 * Added PECL URI template extension and using as default parser if available
63047 * Bug: Fixed Content-Length parsing of Response factory
63048 * Adding rewind() method to entity bodies and streams. Allows for custom rewinding of non-repeatable streams.
63049 * Adding ToArrayInterface throughout library
63050 * Fixing OauthPlugin to create unique nonce values per request
63051
63052 3.0.2 (2012-10-25)
63053 ------------------
63054
63055 * Magic methods are enabled by default on clients
63056 * Magic methods return the result of a command
63057 * Service clients no longer require a base_url option in the factory
63058 * Bug: Fixed an issue with URI templates where null template variables were being expanded
63059
63060 3.0.1 (2012-10-22)
63061 ------------------
63062
63063 * Models can now be used like regular collection objects by calling filter, map, etc
63064 * Models no longer require a Parameter structure or initial data in the constructor
63065 * Added a custom AppendIterator to get around a PHP bug with the `\AppendIterator`
63066
63067 3.0.0 (2012-10-15)
63068 ------------------
63069
63070 * Rewrote service description format to be based on Swagger
63071 * Now based on JSON schema
63072 * Added nested input structures and nested response models
63073 * Support for JSON and XML input and output models
63074 * Renamed `commands` to `operations`
63075 * Removed dot class notation
63076 * Removed custom types
63077 * Broke the project into smaller top-level namespaces to be more component friendly
63078 * Removed support for XML configs and descriptions. Use arrays or JSON files.
63079 * Removed the Validation component and Inspector
63080 * Moved all cookie code to Guzzle\Plugin\Cookie
63081 * Magic methods on a Guzzle\Service\Client now return the command un-executed.
63082 * Calling getResult() or getResponse() on a command will lazily execute the command if needed.
63083 * Now shipping with cURL's CA certs and using it by default
63084 * Added previousResponse() method to response objects
63085 * No longer sending Accept and Accept-Encoding headers on every request
63086 * Only sending an Expect header by default when a payload is greater than 1MB
63087 * Added/moved client options:
63088 * curl.blacklist to curl.option.blacklist
63089 * Added ssl.certificate_authority
63090 * Added a Guzzle\Iterator component
63091 * Moved plugins from Guzzle\Http\Plugin to Guzzle\Plugin
63092 * Added a more robust backoff retry strategy (replaced the ExponentialBackoffPlugin)
63093 * Added a more robust caching plugin
63094 * Added setBody to response objects
63095 * Updating LogPlugin to use a more flexible MessageFormatter
63096 * Added a completely revamped build process
63097 * Cleaning up Collection class and removing default values from the get method
63098 * Fixed ZF2 cache adapters
63099
63100 2.8.8 (2012-10-15)
63101 ------------------
63102
63103 * Bug: Fixed a cookie issue that caused dot prefixed domains to not match where popular browsers did
63104
63105 2.8.7 (2012-09-30)
63106 ------------------
63107
63108 * Bug: Fixed config file aliases for JSON includes
63109 * Bug: Fixed cookie bug on a request object by using CookieParser to parse cookies on requests
63110 * Bug: Removing the path to a file when sending a Content-Disposition header on a POST upload
63111 * Bug: Hardening request and response parsing to account for missing parts
63112 * Bug: Fixed PEAR packaging
63113 * Bug: Fixed Request::getInfo
63114 * Bug: Fixed cases where CURLM_CALL_MULTI_PERFORM return codes were causing curl transactions to fail
63115 * Adding the ability for the namespace Iterator factory to look in multiple directories
63116 * Added more getters/setters/removers from service descriptions
63117 * Added the ability to remove POST fields from OAuth signatures
63118 * OAuth plugin now supports 2-legged OAuth
63119
63120 2.8.6 (2012-09-05)
63121 ------------------
63122
63123 * Added the ability to modify and build service descriptions
63124 * Added the use of visitors to apply parameters to locations in service descriptions using the dynamic command
63125 * Added a `json` parameter location
63126 * Now allowing dot notation for classes in the CacheAdapterFactory
63127 * Using the union of two arrays rather than an array_merge when extending service builder services and service params
63128 * Ensuring that a service is a string before doing strpos() checks on it when substituting services for references
63129 in service builder config files.
63130 * Services defined in two different config files that include one another will by default replace the previously
63131 defined service, but you can now create services that extend themselves and merge their settings over the previous
63132 * The JsonLoader now supports aliasing filenames with different filenames. This allows you to alias something like
63133 '_default' with a default JSON configuration file.
63134
63135 2.8.5 (2012-08-29)
63136 ------------------
63137
63138 * Bug: Suppressed empty arrays from URI templates
63139 * Bug: Added the missing $options argument from ServiceDescription::factory to enable caching
63140 * Added support for HTTP responses that do not contain a reason phrase in the start-line
63141 * AbstractCommand commands are now invokable
63142 * Added a way to get the data used when signing an Oauth request before a request is sent
63143
63144 2.8.4 (2012-08-15)
63145 ------------------
63146
63147 * Bug: Custom delay time calculations are no longer ignored in the ExponentialBackoffPlugin
63148 * Added the ability to transfer entity bodies as a string rather than streamed. This gets around curl error 65. Set `body_as_string` in a request's curl options to enable.
63149 * Added a StreamInterface, EntityBodyInterface, and added ftell() to Guzzle\Common\Stream
63150 * Added an AbstractEntityBodyDecorator and a ReadLimitEntityBody decorator to transfer only a subset of a decorated stream
63151 * Stream and EntityBody objects will now return the file position to the previous position after a read required operation (e.g. getContentMd5())
63152 * Added additional response status codes
63153 * Removed SSL information from the default User-Agent header
63154 * DELETE requests can now send an entity body
63155 * Added an EventDispatcher to the ExponentialBackoffPlugin and added an ExponentialBackoffLogger to log backoff retries
63156 * Added the ability of the MockPlugin to consume mocked request bodies
63157 * LogPlugin now exposes request and response objects in the extras array
63158
63159 2.8.3 (2012-07-30)
63160 ------------------
63161
63162 * Bug: Fixed a case where empty POST requests were sent as GET requests
63163 * Bug: Fixed a bug in ExponentialBackoffPlugin that caused fatal errors when retrying an EntityEnclosingRequest that does not have a body
63164 * Bug: Setting the response body of a request to null after completing a request, not when setting the state of a request to new
63165 * Added multiple inheritance to service description commands
63166 * Added an ApiCommandInterface and added ``getParamNames()`` and ``hasParam()``
63167 * Removed the default 2mb size cutoff from the Md5ValidatorPlugin so that it now defaults to validating everything
63168 * Changed CurlMulti::perform to pass a smaller timeout to CurlMulti::executeHandles
63169
63170 2.8.2 (2012-07-24)
63171 ------------------
63172
63173 * Bug: Query string values set to 0 are no longer dropped from the query string
63174 * Bug: A Collection object is no longer created each time a call is made to ``Guzzle\Service\Command\AbstractCommand::getRequestHeaders()``
63175 * Bug: ``+`` is now treated as an encoded space when parsing query strings
63176 * QueryString and Collection performance improvements
63177 * Allowing dot notation for class paths in filters attribute of a service descriptions
63178
63179 2.8.1 (2012-07-16)
63180 ------------------
63181
63182 * Loosening Event Dispatcher dependency
63183 * POST redirects can now be customized using CURLOPT_POSTREDIR
63184
63185 2.8.0 (2012-07-15)
63186 ------------------
63187
63188 * BC: Guzzle\Http\Query
63189 * Query strings with empty variables will always show an equal sign unless the variable is set to QueryString::BLANK (e.g. ?acl= vs ?acl)
63190 * Changed isEncodingValues() and isEncodingFields() to isUrlEncoding()
63191 * Changed setEncodeValues(bool) and setEncodeFields(bool) to useUrlEncoding(bool)
63192 * Changed the aggregation functions of QueryString to be static methods
63193 * Can now use fromString() with querystrings that have a leading ?
63194 * cURL configuration values can be specified in service descriptions using ``curl.`` prefixed parameters
63195 * Content-Length is set to 0 before emitting the request.before_send event when sending an empty request body
63196 * Cookies are no longer URL decoded by default
63197 * Bug: URI template variables set to null are no longer expanded
63198
63199 2.7.2 (2012-07-02)
63200 ------------------
63201
63202 * BC: Moving things to get ready for subtree splits. Moving Inflection into Common. Moving Guzzle\Http\Parser to Guzzle\Parser.
63203 * BC: Removing Guzzle\Common\Batch\Batch::count() and replacing it with isEmpty()
63204 * CachePlugin now allows for a custom request parameter function to check if a request can be cached
63205 * Bug fix: CachePlugin now only caches GET and HEAD requests by default
63206 * Bug fix: Using header glue when transferring headers over the wire
63207 * Allowing deeply nested arrays for composite variables in URI templates
63208 * Batch divisors can now return iterators or arrays
63209
63210 2.7.1 (2012-06-26)
63211 ------------------
63212
63213 * Minor patch to update version number in UA string
63214 * Updating build process
63215
63216 2.7.0 (2012-06-25)
63217 ------------------
63218
63219 * BC: Inflection classes moved to Guzzle\Inflection. No longer static methods. Can now inject custom inflectors into classes.
63220 * BC: Removed magic setX methods from commands
63221 * BC: Magic methods mapped to service description commands are now inflected in the command factory rather than the client __call() method
63222 * Verbose cURL options are no longer enabled by default. Set curl.debug to true on a client to enable.
63223 * Bug: Now allowing colons in a response start-line (e.g. HTTP/1.1 503 Service Unavailable: Back-end server is at capacity)
63224 * Guzzle\Service\Resource\ResourceIteratorApplyBatched now internally uses the Guzzle\Common\Batch namespace
63225 * Added Guzzle\Service\Plugin namespace and a PluginCollectionPlugin
63226 * Added the ability to set POST fields and files in a service description
63227 * Guzzle\Http\EntityBody::factory() now accepts objects with a __toString() method
63228 * Adding a command.before_prepare event to clients
63229 * Added BatchClosureTransfer and BatchClosureDivisor
63230 * BatchTransferException now includes references to the batch divisor and transfer strategies
63231 * Fixed some tests so that they pass more reliably
63232 * Added Guzzle\Common\Log\ArrayLogAdapter
63233
63234 2.6.6 (2012-06-10)
63235 ------------------
63236
63237 * BC: Removing Guzzle\Http\Plugin\BatchQueuePlugin
63238 * BC: Removing Guzzle\Service\Command\CommandSet
63239 * Adding generic batching system (replaces the batch queue plugin and command set)
63240 * Updating ZF cache and log adapters and now using ZF's composer repository
63241 * Bug: Setting the name of each ApiParam when creating through an ApiCommand
63242 * Adding result_type, result_doc, deprecated, and doc_url to service descriptions
63243 * Bug: Changed the default cookie header casing back to 'Cookie'
63244
63245 2.6.5 (2012-06-03)
63246 ------------------
63247
63248 * BC: Renaming Guzzle\Http\Message\RequestInterface::getResourceUri() to getResource()
63249 * BC: Removing unused AUTH_BASIC and AUTH_DIGEST constants from
63250 * BC: Guzzle\Http\Cookie is now used to manage Set-Cookie data, not Cookie data
63251 * BC: Renaming methods in the CookieJarInterface
63252 * Moving almost all cookie logic out of the CookiePlugin and into the Cookie or CookieJar implementations
63253 * Making the default glue for HTTP headers ';' instead of ','
63254 * Adding a removeValue to Guzzle\Http\Message\Header
63255 * Adding getCookies() to request interface.
63256 * Making it easier to add event subscribers to HasDispatcherInterface classes. Can now directly call addSubscriber()
63257
63258 2.6.4 (2012-05-30)
63259 ------------------
63260
63261 * BC: Cleaning up how POST files are stored in EntityEnclosingRequest objects. Adding PostFile class.
63262 * BC: Moving ApiCommand specific functionality from the Inspector and on to the ApiCommand
63263 * Bug: Fixing magic method command calls on clients
63264 * Bug: Email constraint only validates strings
63265 * Bug: Aggregate POST fields when POST files are present in curl handle
63266 * Bug: Fixing default User-Agent header
63267 * Bug: Only appending or prepending parameters in commands if they are specified
63268 * Bug: Not requiring response reason phrases or status codes to match a predefined list of codes
63269 * Allowing the use of dot notation for class namespaces when using instance_of constraint
63270 * Added any_match validation constraint
63271 * Added an AsyncPlugin
63272 * Passing request object to the calculateWait method of the ExponentialBackoffPlugin
63273 * Allowing the result of a command object to be changed
63274 * Parsing location and type sub values when instantiating a service description rather than over and over at runtime
63275
63276 2.6.3 (2012-05-23)
63277 ------------------
63278
63279 * [BC] Guzzle\Common\FromConfigInterface no longer requires any config options.
63280 * [BC] Refactoring how POST files are stored on an EntityEnclosingRequest. They are now separate from POST fields.
63281 * You can now use an array of data when creating PUT request bodies in the request factory.
63282 * Removing the requirement that HTTPS requests needed a Cache-Control: public directive to be cacheable.
63283 * [Http] Adding support for Content-Type in multipart POST uploads per upload
63284 * [Http] Added support for uploading multiple files using the same name (foo[0], foo[1])
63285 * Adding more POST data operations for easier manipulation of POST data.
63286 * You can now set empty POST fields.
63287 * The body of a request is only shown on EntityEnclosingRequest objects that do not use POST files.
63288 * Split the Guzzle\Service\Inspector::validateConfig method into two methods. One to initialize when a command is created, and one to validate.
63289 * CS updates
63290
63291 2.6.2 (2012-05-19)
63292 ------------------
63293
63294 * [Http] Better handling of nested scope requests in CurlMulti. Requests are now always prepares in the send() method rather than the addRequest() method.
63295
63296 2.6.1 (2012-05-19)
63297 ------------------
63298
63299 * [BC] Removing 'path' support in service descriptions. Use 'uri'.
63300 * [BC] Guzzle\Service\Inspector::parseDocBlock is now protected. Adding getApiParamsForClass() with cache.
63301 * [BC] Removing Guzzle\Common\NullObject. Use https://github.com/mtdowling/NullObject if you need it.
63302 * [BC] Removing Guzzle\Common\XmlElement.
63303 * All commands, both dynamic and concrete, have ApiCommand objects.
63304 * Adding a fix for CurlMulti so that if all of the connections encounter some sort of curl error, then the loop exits.
63305 * Adding checks to EntityEnclosingRequest so that empty POST files and fields are ignored.
63306 * Making the method signature of Guzzle\Service\Builder\ServiceBuilder::factory more flexible.
63307
63308 2.6.0 (2012-05-15)
63309 ------------------
63310
63311 * [BC] Moving Guzzle\Service\Builder to Guzzle\Service\Builder\ServiceBuilder
63312 * [BC] Executing a Command returns the result of the command rather than the command
63313 * [BC] Moving all HTTP parsing logic to Guzzle\Http\Parsers. Allows for faster C implementations if needed.
63314 * [BC] Changing the Guzzle\Http\Message\Response::setProtocol() method to accept a protocol and version in separate args.
63315 * [BC] Moving ResourceIterator* to Guzzle\Service\Resource
63316 * [BC] Completely refactored ResourceIterators to iterate over a cloned command object
63317 * [BC] Moved Guzzle\Http\UriTemplate to Guzzle\Http\Parser\UriTemplate\UriTemplate
63318 * [BC] Guzzle\Guzzle is now deprecated
63319 * Moving Guzzle\Common\Guzzle::inject to Guzzle\Common\Collection::inject
63320 * Adding Guzzle\Version class to give version information about Guzzle
63321 * Adding Guzzle\Http\Utils class to provide getDefaultUserAgent() and getHttpDate()
63322 * Adding Guzzle\Curl\CurlVersion to manage caching curl_version() data
63323 * ServiceDescription and ServiceBuilder are now cacheable using similar configs
63324 * Changing the format of XML and JSON service builder configs. Backwards compatible.
63325 * Cleaned up Cookie parsing
63326 * Trimming the default Guzzle User-Agent header
63327 * Adding a setOnComplete() method to Commands that is called when a command completes
63328 * Keeping track of requests that were mocked in the MockPlugin
63329 * Fixed a caching bug in the CacheAdapterFactory
63330 * Inspector objects can be injected into a Command object
63331 * Refactoring a lot of code and tests to be case insensitive when dealing with headers
63332 * Adding Guzzle\Http\Message\HeaderComparison for easy comparison of HTTP headers using a DSL
63333 * Adding the ability to set global option overrides to service builder configs
63334 * Adding the ability to include other service builder config files from within XML and JSON files
63335 * Moving the parseQuery method out of Url and on to QueryString::fromString() as a static factory method.
63336
63337 2.5.0 (2012-05-08)
63338 ------------------
63339
63340 * Major performance improvements
63341 * [BC] Simplifying Guzzle\Common\Collection. Please check to see if you are using features that are now deprecated.
63342 * [BC] Using a custom validation system that allows a flyweight implementation for much faster validation. No longer using Symfony2 Validation component.
63343 * [BC] No longer supporting "{{ }}" for injecting into command or UriTemplates. Use "{}"
63344 * Added the ability to passed parameters to all requests created by a client
63345 * Added callback functionality to the ExponentialBackoffPlugin
63346 * Using microtime in ExponentialBackoffPlugin to allow more granular backoff strategies.
63347 * Rewinding request stream bodies when retrying requests
63348 * Exception is thrown when JSON response body cannot be decoded
63349 * Added configurable magic method calls to clients and commands. This is off by default.
63350 * Fixed a defect that added a hash to every parsed URL part
63351 * Fixed duplicate none generation for OauthPlugin.
63352 * Emitting an event each time a client is generated by a ServiceBuilder
63353 * Using an ApiParams object instead of a Collection for parameters of an ApiCommand
63354 * cache.* request parameters should be renamed to params.cache.*
63355 * Added the ability to set arbitrary curl options on requests (disable_wire, progress, etc). See CurlHandle.
63356 * Added the ability to disable type validation of service descriptions
63357 * ServiceDescriptions and ServiceBuilders are now Serializable
63358 {
63359 "name": "guzzle/guzzle",
63360 "type": "library",
63361 "description": "Guzzle is a PHP HTTP client library and framework for building RESTful web service clients",
63362 "keywords": ["framework", "http", "rest", "web service", "curl", "client", "HTTP client"],
63363 "homepage": "http://guzzlephp.org/",
63364 "license": "MIT",
63365
63366 "authors": [
63367 {
63368 "name": "Michael Dowling",
63369 "email": "mtdowling@gmail.com",
63370 "homepage": "https://github.com/mtdowling"
63371 },
63372 {
63373 "name": "Guzzle Community",
63374 "homepage": "https://github.com/guzzle/guzzle/contributors"
63375 }
63376 ],
63377
63378 "replace": {
63379 "guzzle/batch": "self.version",
63380 "guzzle/cache": "self.version",
63381 "guzzle/common": "self.version",
63382 "guzzle/http": "self.version",
63383 "guzzle/inflection": "self.version",
63384 "guzzle/iterator": "self.version",
63385 "guzzle/log": "self.version",
63386 "guzzle/parser": "self.version",
63387 "guzzle/plugin": "self.version",
63388 "guzzle/plugin-async": "self.version",
63389 "guzzle/plugin-backoff": "self.version",
63390 "guzzle/plugin-cache": "self.version",
63391 "guzzle/plugin-cookie": "self.version",
63392 "guzzle/plugin-curlauth": "self.version",
63393 "guzzle/plugin-error-response": "self.version",
63394 "guzzle/plugin-history": "self.version",
63395 "guzzle/plugin-log": "self.version",
63396 "guzzle/plugin-md5": "self.version",
63397 "guzzle/plugin-mock": "self.version",
63398 "guzzle/plugin-oauth": "self.version",
63399 "guzzle/service": "self.version",
63400 "guzzle/stream": "self.version"
63401 },
63402
63403 "require": {
63404 "php": ">=5.3.2",
63405 "ext-curl": "*",
63406 "symfony/event-dispatcher": ">=2.1"
63407 },
63408
63409 "autoload": {
63410 "psr-0": {
63411 "Guzzle\\Tests": "tests/",
63412 "Guzzle": "src/"
63413 }
63414 },
63415
63416 "require-dev": {
63417 "doctrine/cache": "*",
63418 "symfony/class-loader": "*",
63419 "monolog/monolog": "1.*",
63420 "psr/log": "1.0.*",
63421 "zendframework/zend-cache": "2.0.*",
63422 "zendframework/zend-log": "2.0.*",
63423 "phpunit/phpunit": "3.7.*"
63424 },
63425
63426 "extra": {
63427 "branch-alias": {
63428 "dev-master": "3.7-dev"
63429 }
63430 }
63431 }
63432 <?xml version="1.0" encoding="UTF-8"?>
63433 <phpunit bootstrap="./tests/bootstrap.php"
63434 colors="true"
63435 processIsolation="false"
63436 stopOnFailure="false"
63437 syntaxCheck="false"
63438 convertErrorsToExceptions="true"
63439 convertNoticesToExceptions="true"
63440 convertWarningsToExceptions="true"
63441 testSuiteLoaderClass="PHPUnit_Runner_StandardTestSuiteLoader">
63442
63443 <testsuites>
63444 <testsuite>
63445 <directory>./tests/Guzzle/Tests</directory>
63446 </testsuite>
63447 </testsuites>
63448
63449 <logging>
63450 <log type="junit" target="build/artifacts/logs/junit.xml" logIncompleteSkipped="false" />
63451 </logging>
63452
63453 <filter>
63454 <whitelist>
63455 <directory suffix=".php">./src/Guzzle</directory>
63456 <exclude>
63457 <directory suffix="Interface.php">./src/Guzzle</directory>
63458 <file>./src/Guzzle/Common/Exception/GuzzleException.php</file>
63459 <file>./src/Guzzle/Http/Exception/HttpException.php</file>
63460 <file>./src/Guzzle/Http/Exception/ServerErrorResponseException.php</file>
63461 <file>./src/Guzzle/Http/Exception/ClientErrorResponseException.php</file>
63462 <file>./src/Guzzle/Http/Exception/TooManyRedirectsException.php</file>
63463 <file>./src/Guzzle/Http/Exception/CouldNotRewindStreamException.php</file>
63464 <file>./src/Guzzle/Common/Exception/BadMethodCallException.php</file>
63465 <file>./src/Guzzle/Common/Exception/InvalidArgumentException.php</file>
63466 <file>./src/Guzzle/Common/Exception/RuntimeException.php</file>
63467 <file>./src/Guzzle/Common/Exception/UnexpectedValueException.php</file>
63468 <file>./src/Guzzle/Service/Exception/ClientNotFoundException.php</file>
63469 <file>./src/Guzzle/Service/Exception/CommandException.php</file>
63470 <file>./src/Guzzle/Service/Exception/DescriptionBuilderException.php</file>
63471 <file>./src/Guzzle/Service/Exception/ServiceBuilderException.php</file>
63472 <file>./src/Guzzle/Service/Exception/ServiceNotFoundException.php</file>
63473 <file>./src/Guzzle/Service/Exception/ValidationException.php</file>
63474 <file>./src/Guzzle/Service/Exception/JsonException.php</file>
63475 </exclude>
63476 </whitelist>
63477 </filter>
63478
63479 </phpunit>
63480 Guzzle Upgrade Guide
63481 ====================
63482
63483 3.6 to 3.7
63484 ----------
63485
63486 ### Deprecations
63487
63488 - You can now enable E_USER_DEPRECATED warnings to see if you are using any deprecated methods.:
63489
63490 ```php
63491 \Guzzle\Common\Version::$emitWarnings = true;
63492 ```
63493
63494 The following APIs and options have been marked as deprecated:
63495
63496 - Marked `Guzzle\Http\Message\Request::isResponseBodyRepeatable()` as deprecated. Use `$request->getResponseBody()->isRepeatable()` instead.
63497 - Marked `Guzzle\Http\Message\Request::canCache()` as deprecated. Use `Guzzle\Plugin\Cache\DefaultCanCacheStrategy->canCacheRequest()` instead.
63498 - Marked `Guzzle\Http\Message\Request::canCache()` as deprecated. Use `Guzzle\Plugin\Cache\DefaultCanCacheStrategy->canCacheRequest()` instead.
63499 - Marked `Guzzle\Http\Message\Request::setIsRedirect()` as deprecated. Use the HistoryPlugin instead.
63500 - Marked `Guzzle\Http\Message\Request::isRedirect()` as deprecated. Use the HistoryPlugin instead.
63501 - Marked `Guzzle\Cache\CacheAdapterFactory::factory()` as deprecated
63502 - Marked `Guzzle\Service\Client::enableMagicMethods()` as deprecated. Magic methods can no longer be disabled on a Guzzle\Service\Client.
63503 - Marked `Guzzle\Parser\Url\UrlParser` as deprecated. Just use PHP's `parse_url()` and percent encode your UTF-8.
63504 - Marked `Guzzle\Common\Collection::inject()` as deprecated.
63505 - Marked `Guzzle\Plugin\CurlAuth\CurlAuthPlugin` as deprecated. Use
63506 `$client->getConfig()->setPath('request.options/auth', array('user', 'pass', 'Basic|Digest|NTLM|Any'));` or
63507 `$client->setDefaultOption('auth', array('user', 'pass', 'Basic|Digest|NTLM|Any'));`
63508
63509 3.7 introduces `request.options` as a parameter for a client configuration and as an optional argument to all creational
63510 request methods. When paired with a client's configuration settings, these options allow you to specify default settings
63511 for various aspects of a request. Because these options make other previous configuration options redundant, several
63512 configuration options and methods of a client and AbstractCommand have been deprecated.
63513
63514 - Marked `Guzzle\Service\Client::getDefaultHeaders()` as deprecated. Use `$client->getDefaultOption('headers')`.
63515 - Marked `Guzzle\Service\Client::setDefaultHeaders()` as deprecated. Use `$client->setDefaultOption('headers/{header_name}', 'value')`.
63516 - Marked 'request.params' for `Guzzle\Http\Client` as deprecated. Use `$client->setDefaultOption('params/{param_name}', 'value')`
63517 - Marked 'command.headers', 'command.response_body' and 'command.on_complete' as deprecated for AbstractCommand. These will work through Guzzle 4.0
63518
63519 $command = $client->getCommand('foo', array(
63520 'command.headers' => array('Test' => '123'),
63521 'command.response_body' => '/path/to/file'
63522 ));
63523
63524 // Should be changed to:
63525
63526 $command = $client->getCommand('foo', array(
63527 'command.request_options' => array(
63528 'headers' => array('Test' => '123'),
63529 'save_as' => '/path/to/file'
63530 )
63531 ));
63532
63533 ### Interface changes
63534
63535 Additions and changes (you will need to update any implementations or subclasses you may have created):
63536
63537 - Added an `$options` argument to the end of the following methods of `Guzzle\Http\ClientInterface`:
63538 createRequest, head, delete, put, patch, post, options, prepareRequest
63539 - Added an `$options` argument to the end of `Guzzle\Http\Message\Request\RequestFactoryInterface::createRequest()`
63540 - Added an `applyOptions()` method to `Guzzle\Http\Message\Request\RequestFactoryInterface`
63541 - Changed `Guzzle\Http\ClientInterface::get($uri = null, $headers = null, $body = null)` to
63542 `Guzzle\Http\ClientInterface::get($uri = null, $headers = null, $options = array())`. You can still pass in a
63543 resource, string, or EntityBody into the $options parameter to specify the download location of the response.
63544 - Changed `Guzzle\Common\Collection::__construct($data)` to no longer accepts a null value for `$data` but a
63545 default `array()`
63546 - Added `Guzzle\Stream\StreamInterface::isRepeatable`
63547 - Made `Guzzle\Http\Client::expandTemplate` and `getUriTemplate` protected methods.
63548
63549 The following methods were removed from interfaces. All of these methods are still available in the concrete classes
63550 that implement them, but you should update your code to use alternative methods:
63551
63552 - Removed `Guzzle\Http\ClientInterface::setDefaultHeaders(). Use
63553 `$client->getConfig()->setPath('request.options/headers/{header_name}', 'value')`. or
63554 `$client->getConfig()->setPath('request.options/headers', array('header_name' => 'value'))` or
63555 `$client->setDefaultOption('headers/{header_name}', 'value')`. or
63556 `$client->setDefaultOption('headers', array('header_name' => 'value'))`.
63557 - Removed `Guzzle\Http\ClientInterface::getDefaultHeaders(). Use `$client->getConfig()->getPath('request.options/headers')`.
63558 - Removed `Guzzle\Http\ClientInterface::expandTemplate()`. This is an implementation detail.
63559 - Removed `Guzzle\Http\ClientInterface::setRequestFactory()`. This is an implementation detail.
63560 - Removed `Guzzle\Http\ClientInterface::getCurlMulti()`. This is a very specific implementation detail.
63561 - Removed `Guzzle\Http\Message\RequestInterface::canCache`. Use the CachePlugin.
63562 - Removed `Guzzle\Http\Message\RequestInterface::setIsRedirect`. Use the HistoryPlugin.
63563 - Removed `Guzzle\Http\Message\RequestInterface::isRedirect`. Use the HistoryPlugin.
63564
63565 ### Cache plugin breaking changes
63566
63567 - CacheKeyProviderInterface and DefaultCacheKeyProvider are no longer used. All of this logic is handled in a
63568 CacheStorageInterface. These two objects and interface will be removed in a future version.
63569 - Always setting X-cache headers on cached responses
63570 - Default cache TTLs are now handled by the CacheStorageInterface of a CachePlugin
63571 - `CacheStorageInterface::cache($key, Response $response, $ttl = null)` has changed to `cache(RequestInterface
63572 $request, Response $response);`
63573 - `CacheStorageInterface::fetch($key)` has changed to `fetch(RequestInterface $request);`
63574 - `CacheStorageInterface::delete($key)` has changed to `delete(RequestInterface $request);`
63575 - Added `CacheStorageInterface::purge($url)`
63576 - `DefaultRevalidation::__construct(CacheKeyProviderInterface $cacheKey, CacheStorageInterface $cache, CachePlugin
63577 $plugin)` has changed to `DefaultRevalidation::__construct(CacheStorageInterface $cache,
63578 CanCacheStrategyInterface $canCache = null)`
63579 - Added `RevalidationInterface::shouldRevalidate(RequestInterface $request, Response $response)`
63580
63581 3.5 to 3.6
63582 ----------
63583
63584 * Mixed casing of headers are now forced to be a single consistent casing across all values for that header.
63585 * Messages internally use a HeaderCollection object to delegate handling case-insensitive header resolution
63586 * Removed the whole changedHeader() function system of messages because all header changes now go through addHeader().
63587 For example, setHeader() first removes the header using unset on a HeaderCollection and then calls addHeader().
63588 Keeping the Host header and URL host in sync is now handled by overriding the addHeader method in Request.
63589 * Specific header implementations can be created for complex headers. When a message creates a header, it uses a
63590 HeaderFactory which can map specific headers to specific header classes. There is now a Link header and
63591 CacheControl header implementation.
63592 * Moved getLinks() from Response to just be used on a Link header object.
63593
63594 If you previously relied on Guzzle\Http\Message\Header::raw(), then you will need to update your code to use the
63595 HeaderInterface (e.g. toArray(), getAll(), etc).
63596
63597 ### Interface changes
63598
63599 * Removed from interface: Guzzle\Http\ClientInterface::setUriTemplate
63600 * Removed from interface: Guzzle\Http\ClientInterface::setCurlMulti()
63601 * Removed Guzzle\Http\Message\Request::receivedRequestHeader() and implemented this functionality in
63602 Guzzle\Http\Curl\RequestMediator
63603 * Removed the optional $asString parameter from MessageInterface::getHeader(). Just cast the header to a string.
63604 * Removed the optional $tryChunkedTransfer option from Guzzle\Http\Message\EntityEnclosingRequestInterface
63605 * Removed the $asObjects argument from Guzzle\Http\Message\MessageInterface::getHeaders()
63606
63607 ### Removed deprecated functions
63608
63609 * Removed Guzzle\Parser\ParserRegister::get(). Use getParser()
63610 * Removed Guzzle\Parser\ParserRegister::set(). Use registerParser().
63611
63612 ### Deprecations
63613
63614 * The ability to case-insensitively search for header values
63615 * Guzzle\Http\Message\Header::hasExactHeader
63616 * Guzzle\Http\Message\Header::raw. Use getAll()
63617 * Deprecated cache control specific methods on Guzzle\Http\Message\AbstractMessage. Use the CacheControl header object
63618 instead.
63619
63620 ### Other changes
63621
63622 * All response header helper functions return a string rather than mixing Header objects and strings inconsistently
63623 * Removed cURL blacklist support. This is no longer necessary now that Expect, Accept, etc are managed by Guzzle
63624 directly via interfaces
63625 * Removed the injecting of a request object onto a response object. The methods to get and set a request still exist
63626 but are a no-op until removed.
63627 * Most classes that used to require a ``Guzzle\Service\Command\CommandInterface` typehint now request a
63628 `Guzzle\Service\Command\ArrayCommandInterface`.
63629 * Added `Guzzle\Http\Message\RequestInterface::startResponse()` to the RequestInterface to handle injecting a response
63630 on a request while the request is still being transferred
63631 * `Guzzle\Service\Command\CommandInterface` now extends from ToArrayInterface and ArrayAccess
63632
63633 3.3 to 3.4
63634 ----------
63635
63636 Base URLs of a client now follow the rules of http://tools.ietf.org/html/rfc3986#section-5.2.2 when merging URLs.
63637
63638 3.2 to 3.3
63639 ----------
63640
63641 ### Response::getEtag() quote stripping removed
63642
63643 `Guzzle\Http\Message\Response::getEtag()` no longer strips quotes around the ETag response header
63644
63645 ### Removed `Guzzle\Http\Utils`
63646
63647 The `Guzzle\Http\Utils` class was removed. This class was only used for testing.
63648
63649 ### Stream wrapper and type
63650
63651 `Guzzle\Stream\Stream::getWrapper()` and `Guzzle\Stream\Stream::getSteamType()` are no longer converted to lowercase.
63652
63653 ### curl.emit_io became emit_io
63654
63655 Emitting IO events from a RequestMediator is now a parameter that must be set in a request's curl options using the
63656 'emit_io' key. This was previously set under a request's parameters using 'curl.emit_io'
63657
63658 3.1 to 3.2
63659 ----------
63660
63661 ### CurlMulti is no longer reused globally
63662
63663 Before 3.2, the same CurlMulti object was reused globally for each client. This can cause issue where plugins added
63664 to a single client can pollute requests dispatched from other clients.
63665
63666 If you still wish to reuse the same CurlMulti object with each client, then you can add a listener to the
63667 ServiceBuilder's `service_builder.create_client` event to inject a custom CurlMulti object into each client as it is
63668 created.
63669
63670 ```php
63671 $multi = new Guzzle\Http\Curl\CurlMulti();
63672 $builder = Guzzle\Service\Builder\ServiceBuilder::factory('/path/to/config.json');
63673 $builder->addListener('service_builder.create_client', function ($event) use ($multi) {
63674 $event['client']->setCurlMulti($multi);
63675 }
63676 });
63677 ```
63678
63679 ### No default path
63680
63681 URLs no longer have a default path value of '/' if no path was specified.
63682
63683 Before:
63684
63685 ```php
63686 $request = $client->get('http://www.foo.com');
63687 echo $request->getUrl();
63688 // >> http://www.foo.com/
63689 ```
63690
63691 After:
63692
63693 ```php
63694 $request = $client->get('http://www.foo.com');
63695 echo $request->getUrl();
63696 // >> http://www.foo.com
63697 ```
63698
63699 ### Less verbose BadResponseException
63700
63701 The exception message for `Guzzle\Http\Exception\BadResponseException` no longer contains the full HTTP request and
63702 response information. You can, however, get access to the request and response object by calling `getRequest()` or
63703 `getResponse()` on the exception object.
63704
63705
63706 ### Query parameter aggregation
63707
63708 Multi-valued query parameters are no longer aggregated using a callback function. `Guzzle\Http\Query` now has a
63709 setAggregator() method that accepts a `Guzzle\Http\QueryAggregator\QueryAggregatorInterface` object. This object is
63710 responsible for handling the aggregation of multi-valued query string variables into a flattened hash.
63711
63712 2.8 to 3.x
63713 ----------
63714
63715 ### Guzzle\Service\Inspector
63716
63717 Change `\Guzzle\Service\Inspector::fromConfig` to `\Guzzle\Common\Collection::fromConfig`
63718
63719 **Before**
63720
63721 ```php
63722 use Guzzle\Service\Inspector;
63723
63724 class YourClient extends \Guzzle\Service\Client
63725 {
63726 public static function factory($config = array())
63727 {
63728 $default = array();
63729 $required = array('base_url', 'username', 'api_key');
63730 $config = Inspector::fromConfig($config, $default, $required);
63731
63732 $client = new self(
63733 $config->get('base_url'),
63734 $config->get('username'),
63735 $config->get('api_key')
63736 );
63737 $client->setConfig($config);
63738
63739 $client->setDescription(ServiceDescription::factory(__DIR__ . DIRECTORY_SEPARATOR . 'client.json'));
63740
63741 return $client;
63742 }
63743 ```
63744
63745 **After**
63746
63747 ```php
63748 use Guzzle\Common\Collection;
63749
63750 class YourClient extends \Guzzle\Service\Client
63751 {
63752 public static function factory($config = array())
63753 {
63754 $default = array();
63755 $required = array('base_url', 'username', 'api_key');
63756 $config = Collection::fromConfig($config, $default, $required);
63757
63758 $client = new self(
63759 $config->get('base_url'),
63760 $config->get('username'),
63761 $config->get('api_key')
63762 );
63763 $client->setConfig($config);
63764
63765 $client->setDescription(ServiceDescription::factory(__DIR__ . DIRECTORY_SEPARATOR . 'client.json'));
63766
63767 return $client;
63768 }
63769 ```
63770
63771 ### Convert XML Service Descriptions to JSON
63772
63773 **Before**
63774
63775 ```xml
63776 <?xml version="1.0" encoding="UTF-8"?>
63777 <client>
63778 <commands>
63779 <!-- Groups -->
63780 <command name="list_groups" method="GET" uri="groups.json">
63781 <doc>Get a list of groups</doc>
63782 </command>
63783 <command name="search_groups" method="GET" uri='search.json?query="{{query}} type:group"'>
63784 <doc>Uses a search query to get a list of groups</doc>
63785 <param name="query" type="string" required="true" />
63786 </command>
63787 <command name="create_group" method="POST" uri="groups.json">
63788 <doc>Create a group</doc>
63789 <param name="data" type="array" location="body" filters="json_encode" doc="Group JSON"/>
63790 <param name="Content-Type" location="header" static="application/json"/>
63791 </command>
63792 <command name="delete_group" method="DELETE" uri="groups/{{id}}.json">
63793 <doc>Delete a group by ID</doc>
63794 <param name="id" type="integer" required="true"/>
63795 </command>
63796 <command name="get_group" method="GET" uri="groups/{{id}}.json">
63797 <param name="id" type="integer" required="true"/>
63798 </command>
63799 <command name="update_group" method="PUT" uri="groups/{{id}}.json">
63800 <doc>Update a group</doc>
63801 <param name="id" type="integer" required="true"/>
63802 <param name="data" type="array" location="body" filters="json_encode" doc="Group JSON"/>
63803 <param name="Content-Type" location="header" static="application/json"/>
63804 </command>
63805 </commands>
63806 </client>
63807 ```
63808
63809 **After**
63810
63811 ```json
63812 {
63813 "name": "Zendesk REST API v2",
63814 "apiVersion": "2012-12-31",
63815 "description":"Provides access to Zendesk views, groups, tickets, ticket fields, and users",
63816 "operations": {
63817 "list_groups": {
63818 "httpMethod":"GET",
63819 "uri": "groups.json",
63820 "summary": "Get a list of groups"
63821 },
63822 "search_groups":{
63823 "httpMethod":"GET",
63824 "uri": "search.json?query=\"{query} type:group\"",
63825 "summary": "Uses a search query to get a list of groups",
63826 "parameters":{
63827 "query":{
63828 "location": "uri",
63829 "description":"Zendesk Search Query",
63830 "type": "string",
63831 "required": true
63832 }
63833 }
63834 },
63835 "create_group": {
63836 "httpMethod":"POST",
63837 "uri": "groups.json",
63838 "summary": "Create a group",
63839 "parameters":{
63840 "data": {
63841 "type": "array",
63842 "location": "body",
63843 "description":"Group JSON",
63844 "filters": "json_encode",
63845 "required": true
63846 },
63847 "Content-Type":{
63848 "type": "string",
63849 "location":"header",
63850 "static": "application/json"
63851 }
63852 }
63853 },
63854 "delete_group": {
63855 "httpMethod":"DELETE",
63856 "uri": "groups/{id}.json",
63857 "summary": "Delete a group",
63858 "parameters":{
63859 "id":{
63860 "location": "uri",
63861 "description":"Group to delete by ID",
63862 "type": "integer",
63863 "required": true
63864 }
63865 }
63866 },
63867 "get_group": {
63868 "httpMethod":"GET",
63869 "uri": "groups/{id}.json",
63870 "summary": "Get a ticket",
63871 "parameters":{
63872 "id":{
63873 "location": "uri",
63874 "description":"Group to get by ID",
63875 "type": "integer",
63876 "required": true
63877 }
63878 }
63879 },
63880 "update_group": {
63881 "httpMethod":"PUT",
63882 "uri": "groups/{id}.json",
63883 "summary": "Update a group",
63884 "parameters":{
63885 "id": {
63886 "location": "uri",
63887 "description":"Group to update by ID",
63888 "type": "integer",
63889 "required": true
63890 },
63891 "data": {
63892 "type": "array",
63893 "location": "body",
63894 "description":"Group JSON",
63895 "filters": "json_encode",
63896 "required": true
63897 },
63898 "Content-Type":{
63899 "type": "string",
63900 "location":"header",
63901 "static": "application/json"
63902 }
63903 }
63904 }
63905 }
63906 ```
63907
63908 ### Guzzle\Service\Description\ServiceDescription
63909
63910 Commands are now called Operations
63911
63912 **Before**
63913
63914 ```php
63915 use Guzzle\Service\Description\ServiceDescription;
63916
63917 $sd = new ServiceDescription();
63918 $sd->getCommands(); // @returns ApiCommandInterface[]
63919 $sd->hasCommand($name);
63920 $sd->getCommand($name); // @returns ApiCommandInterface|null
63921 $sd->addCommand($command); // @param ApiCommandInterface $command
63922 ```
63923
63924 **After**
63925
63926 ```php
63927 use Guzzle\Service\Description\ServiceDescription;
63928
63929 $sd = new ServiceDescription();
63930 $sd->getOperations(); // @returns OperationInterface[]
63931 $sd->hasOperation($name);
63932 $sd->getOperation($name); // @returns OperationInterface|null
63933 $sd->addOperation($operation); // @param OperationInterface $operation
63934 ```
63935
63936 ### Guzzle\Common\Inflection\Inflector
63937
63938 Namespace is now `Guzzle\Inflection\Inflector`
63939
63940 ### Guzzle\Http\Plugin
63941
63942 Namespace is now `Guzzle\Plugin`. Many other changes occur within this namespace and are detailed in their own sections below.
63943
63944 ### Guzzle\Http\Plugin\LogPlugin and Guzzle\Common\Log
63945
63946 Now `Guzzle\Plugin\Log\LogPlugin` and `Guzzle\Log` respectively.
63947
63948 **Before**
63949
63950 ```php
63951 use Guzzle\Common\Log\ClosureLogAdapter;
63952 use Guzzle\Http\Plugin\LogPlugin;
63953
63954 /** @var \Guzzle\Http\Client */
63955 $client;
63956
63957 // $verbosity is an integer indicating desired message verbosity level
63958 $client->addSubscriber(new LogPlugin(new ClosureLogAdapter(function($m) { echo $m; }, $verbosity = LogPlugin::LOG_VERBOSE);
63959 ```
63960
63961 **After**
63962
63963 ```php
63964 use Guzzle\Log\ClosureLogAdapter;
63965 use Guzzle\Log\MessageFormatter;
63966 use Guzzle\Plugin\Log\LogPlugin;
63967
63968 /** @var \Guzzle\Http\Client */
63969 $client;
63970
63971 // $format is a string indicating desired message format -- @see MessageFormatter
63972 $client->addSubscriber(new LogPlugin(new ClosureLogAdapter(function($m) { echo $m; }, $format = MessageFormatter::DEBUG_FORMAT);
63973 ```
63974
63975 ### Guzzle\Http\Plugin\CurlAuthPlugin
63976
63977 Now `Guzzle\Plugin\CurlAuth\CurlAuthPlugin`.
63978
63979 ### Guzzle\Http\Plugin\ExponentialBackoffPlugin
63980
63981 Now `Guzzle\Plugin\Backoff\BackoffPlugin`, and other changes.
63982
63983 **Before**
63984
63985 ```php
63986 use Guzzle\Http\Plugin\ExponentialBackoffPlugin;
63987
63988 $backoffPlugin = new ExponentialBackoffPlugin($maxRetries, array_merge(
63989 ExponentialBackoffPlugin::getDefaultFailureCodes(), array(429)
63990 ));
63991
63992 $client->addSubscriber($backoffPlugin);
63993 ```
63994
63995 **After**
63996
63997 ```php
63998 use Guzzle\Plugin\Backoff\BackoffPlugin;
63999 use Guzzle\Plugin\Backoff\HttpBackoffStrategy;
64000
64001 // Use convenient factory method instead -- see implementation for ideas of what
64002 // you can do with chaining backoff strategies
64003 $backoffPlugin = BackoffPlugin::getExponentialBackoff($maxRetries, array_merge(
64004 HttpBackoffStrategy::getDefaultFailureCodes(), array(429)
64005 ));
64006 $client->addSubscriber($backoffPlugin);
64007 ```
64008
64009
64010 ### Known Issues
64011
64012 #### [BUG] Accept-Encoding header behavior changed unintentionally.
64013
64014 (See #217) (Fixed in 09daeb8c666fb44499a0646d655a8ae36456575e)
64015
64016 In version 2.8 setting the `Accept-Encoding` header would set the CURLOPT_ENCODING option, which permitted cURL to
64017 properly handle gzip/deflate compressed responses from the server. In versions affected by this bug this does not happen.
64018 See issue #217 for a workaround, or use a version containing the fix.
64019 PHP Collection
64020 ==============
64021
64022 Learn more about it in its [documentation](http://jmsyst.com/libs/php-collection).
64023 Apache License
64024 Version 2.0, January 2004
64025 http://www.apache.org/licenses/
64026
64027 TERMS AND CONDITIONS FOR USE, REPRODUCTION, AND DISTRIBUTION
64028
64029 1. Definitions.
64030
64031 "License" shall mean the terms and conditions for use, reproduction,
64032 and distribution as defined by Sections 1 through 9 of this document.
64033
64034 "Licensor" shall mean the copyright owner or entity authorized by
64035 the copyright owner that is granting the License.
64036
64037 "Legal Entity" shall mean the union of the acting entity and all
64038 other entities that control, are controlled by, or are under common
64039 control with that entity. For the purposes of this definition,
64040 "control" means (i) the power, direct or indirect, to cause the
64041 direction or management of such entity, whether by contract or
64042 otherwise, or (ii) ownership of fifty percent (50%) or more of the
64043 outstanding shares, or (iii) beneficial ownership of such entity.
64044
64045 "You" (or "Your") shall mean an individual or Legal Entity
64046 exercising permissions granted by this License.
64047
64048 "Source" form shall mean the preferred form for making modifications,
64049 including but not limited to software source code, documentation
64050 source, and configuration files.
64051
64052 "Object" form shall mean any form resulting from mechanical
64053 transformation or translation of a Source form, including but
64054 not limited to compiled object code, generated documentation,
64055 and conversions to other media types.
64056
64057 "Work" shall mean the work of authorship, whether in Source or
64058 Object form, made available under the License, as indicated by a
64059 copyright notice that is included in or attached to the work
64060 (an example is provided in the Appendix below).
64061
64062 "Derivative Works" shall mean any work, whether in Source or Object
64063 form, that is based on (or derived from) the Work and for which the
64064 editorial revisions, annotations, elaborations, or other modifications
64065 represent, as a whole, an original work of authorship. For the purposes
64066 of this License, Derivative Works shall not include works that remain
64067 separable from, or merely link (or bind by name) to the interfaces of,
64068 the Work and Derivative Works thereof.
64069
64070 "Contribution" shall mean any work of authorship, including
64071 the original version of the Work and any modifications or additions
64072 to that Work or Derivative Works thereof, that is intentionally
64073 submitted to Licensor for inclusion in the Work by the copyright owner
64074 or by an individual or Legal Entity authorized to submit on behalf of
64075 the copyright owner. For the purposes of this definition, "submitted"
64076 means any form of electronic, verbal, or written communication sent
64077 to the Licensor or its representatives, including but not limited to
64078 communication on electronic mailing lists, source code control systems,
64079 and issue tracking systems that are managed by, or on behalf of, the
64080 Licensor for the purpose of discussing and improving the Work, but
64081 excluding communication that is conspicuously marked or otherwise
64082 designated in writing by the copyright owner as "Not a Contribution."
64083
64084 "Contributor" shall mean Licensor and any individual or Legal Entity
64085 on behalf of whom a Contribution has been received by Licensor and
64086 subsequently incorporated within the Work.
64087
64088 2. Grant of Copyright License. Subject to the terms and conditions of
64089 this License, each Contributor hereby grants to You a perpetual,
64090 worldwide, non-exclusive, no-charge, royalty-free, irrevocable
64091 copyright license to reproduce, prepare Derivative Works of,
64092 publicly display, publicly perform, sublicense, and distribute the
64093 Work and such Derivative Works in Source or Object form.
64094
64095 3. Grant of Patent License. Subject to the terms and conditions of
64096 this License, each Contributor hereby grants to You a perpetual,
64097 worldwide, non-exclusive, no-charge, royalty-free, irrevocable
64098 (except as stated in this section) patent license to make, have made,
64099 use, offer to sell, sell, import, and otherwise transfer the Work,
64100 where such license applies only to those patent claims licensable
64101 by such Contributor that are necessarily infringed by their
64102 Contribution(s) alone or by combination of their Contribution(s)
64103 with the Work to which such Contribution(s) was submitted. If You
64104 institute patent litigation against any entity (including a
64105 cross-claim or counterclaim in a lawsuit) alleging that the Work
64106 or a Contribution incorporated within the Work constitutes direct
64107 or contributory patent infringement, then any patent licenses
64108 granted to You under this License for that Work shall terminate
64109 as of the date such litigation is filed.
64110
64111 4. Redistribution. You may reproduce and distribute copies of the
64112 Work or Derivative Works thereof in any medium, with or without
64113 modifications, and in Source or Object form, provided that You
64114 meet the following conditions:
64115
64116 (a) You must give any other recipients of the Work or
64117 Derivative Works a copy of this License; and
64118
64119 (b) You must cause any modified files to carry prominent notices
64120 stating that You changed the files; and
64121
64122 (c) You must retain, in the Source form of any Derivative Works
64123 that You distribute, all copyright, patent, trademark, and
64124 attribution notices from the Source form of the Work,
64125 excluding those notices that do not pertain to any part of
64126 the Derivative Works; and
64127
64128 (d) If the Work includes a "NOTICE" text file as part of its
64129 distribution, then any Derivative Works that You distribute must
64130 include a readable copy of the attribution notices contained
64131 within such NOTICE file, excluding those notices that do not
64132 pertain to any part of the Derivative Works, in at least one
64133 of the following places: within a NOTICE text file distributed
64134 as part of the Derivative Works; within the Source form or
64135 documentation, if provided along with the Derivative Works; or,
64136 within a display generated by the Derivative Works, if and
64137 wherever such third-party notices normally appear. The contents
64138 of the NOTICE file are for informational purposes only and
64139 do not modify the License. You may add Your own attribution
64140 notices within Derivative Works that You distribute, alongside
64141 or as an addendum to the NOTICE text from the Work, provided
64142 that such additional attribution notices cannot be construed
64143 as modifying the License.
64144
64145 You may add Your own copyright statement to Your modifications and
64146 may provide additional or different license terms and conditions
64147 for use, reproduction, or distribution of Your modifications, or
64148 for any such Derivative Works as a whole, provided Your use,
64149 reproduction, and distribution of the Work otherwise complies with
64150 the conditions stated in this License.
64151
64152 5. Submission of Contributions. Unless You explicitly state otherwise,
64153 any Contribution intentionally submitted for inclusion in the Work
64154 by You to the Licensor shall be under the terms and conditions of
64155 this License, without any additional terms or conditions.
64156 Notwithstanding the above, nothing herein shall supersede or modify
64157 the terms of any separate license agreement you may have executed
64158 with Licensor regarding such Contributions.
64159
64160 6. Trademarks. This License does not grant permission to use the trade
64161 names, trademarks, service marks, or product names of the Licensor,
64162 except as required for reasonable and customary use in describing the
64163 origin of the Work and reproducing the content of the NOTICE file.
64164
64165 7. Disclaimer of Warranty. Unless required by applicable law or
64166 agreed to in writing, Licensor provides the Work (and each
64167 Contributor provides its Contributions) on an "AS IS" BASIS,
64168 WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or
64169 implied, including, without limitation, any warranties or conditions
64170 of TITLE, NON-INFRINGEMENT, MERCHANTABILITY, or FITNESS FOR A
64171 PARTICULAR PURPOSE. You are solely responsible for determining the
64172 appropriateness of using or redistributing the Work and assume any
64173 risks associated with Your exercise of permissions under this License.
64174
64175 8. Limitation of Liability. In no event and under no legal theory,
64176 whether in tort (including negligence), contract, or otherwise,
64177 unless required by applicable law (such as deliberate and grossly
64178 negligent acts) or agreed to in writing, shall any Contributor be
64179 liable to You for damages, including any direct, indirect, special,
64180 incidental, or consequential damages of any character arising as a
64181 result of this License or out of the use or inability to use the
64182 Work (including but not limited to damages for loss of goodwill,
64183 work stoppage, computer failure or malfunction, or any and all
64184 other commercial damages or losses), even if such Contributor
64185 has been advised of the possibility of such damages.
64186
64187 9. Accepting Warranty or Additional Liability. While redistributing
64188 the Work or Derivative Works thereof, You may choose to offer,
64189 and charge a fee for, acceptance of support, warranty, indemnity,
64190 or other liability obligations and/or rights consistent with this
64191 License. However, in accepting such obligations, You may act only
64192 on Your own behalf and on Your sole responsibility, not on behalf
64193 of any other Contributor, and only if You agree to indemnify,
64194 defend, and hold each Contributor harmless for any liability
64195 incurred by, or claims asserted against, such Contributor by reason
64196 of your accepting any such warranty or additional liability.
64197
64198 END OF TERMS AND CONDITIONS
64199
64200 APPENDIX: How to apply the Apache License to your work.
64201
64202 To apply the Apache License to your work, attach the following
64203 boilerplate notice, with the fields enclosed by brackets "[]"
64204 replaced with your own identifying information. (Don't include
64205 the brackets!) The text should be enclosed in the appropriate
64206 comment syntax for the file format. We also recommend that a
64207 file or class name and description of purpose be included on the
64208 same "printed page" as the copyright notice for easier
64209 identification within third-party archives.
64210
64211 Copyright [yyyy] [name of copyright owner]
64212
64213 Licensed under the Apache License, Version 2.0 (the "License");
64214 you may not use this file except in compliance with the License.
64215 You may obtain a copy of the License at
64216
64217 http://www.apache.org/licenses/LICENSE-2.0
64218
64219 Unless required by applicable law or agreed to in writing, software
64220 distributed under the License is distributed on an "AS IS" BASIS,
64221 WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
64222 See the License for the specific language governing permissions and
64223 limitations under the License.<?php
64224
64225 /*
64226 * Copyright 2012 Johannes M. Schmitt <schmittjoh@gmail.com>
64227 *
64228 * Licensed under the Apache License, Version 2.0 (the "License");
64229 * you may not use this file except in compliance with the License.
64230 * You may obtain a copy of the License at
64231 *
64232 * http://www.apache.org/licenses/LICENSE-2.0
64233 *
64234 * Unless required by applicable law or agreed to in writing, software
64235 * distributed under the License is distributed on an "AS IS" BASIS,
64236 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
64237 * See the License for the specific language governing permissions and
64238 * limitations under the License.
64239 */
64240
64241 namespace PhpCollection;
64242
64243 use PhpOption\LazyOption;
64244 use PhpOption\Some;
64245 use PhpOption\None;
64246
64247 abstract class AbstractCollection
64248 {
64249 public function contains($searchedElem)
64250 {
64251 foreach ($this as $elem) {
64252 if ($elem === $searchedElem) {
64253 return true;
64254 }
64255 }
64256
64257 return false;
64258 }
64259
64260 public function find($callable)
64261 {
64262 $self = $this;
64263
64264 return new LazyOption(function() use ($callable, $self) {
64265 foreach ($self as $elem) {
64266 if (call_user_func($callable, $elem) === true) {
64267 return new Some($elem);
64268 }
64269 }
64270
64271 return None::create();
64272 });
64273 }
64274 }<?php
64275
64276 /*
64277 * Copyright 2012 Johannes M. Schmitt <schmittjoh@gmail.com>
64278 *
64279 * Licensed under the Apache License, Version 2.0 (the "License");
64280 * you may not use this file except in compliance with the License.
64281 * You may obtain a copy of the License at
64282 *
64283 * http://www.apache.org/licenses/LICENSE-2.0
64284 *
64285 * Unless required by applicable law or agreed to in writing, software
64286 * distributed under the License is distributed on an "AS IS" BASIS,
64287 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
64288 * See the License for the specific language governing permissions and
64289 * limitations under the License.
64290 */
64291
64292 namespace PhpCollection;
64293
64294 class Map extends AbstractMap implements SortableInterface
64295 {
64296 public function sortWith($callable)
64297 {
64298 uksort($this->elements, $callable);
64299 }
64300 }<?php
64301
64302 /*
64303 * Copyright 2012 Johannes M. Schmitt <schmittjoh@gmail.com>
64304 *
64305 * Licensed under the Apache License, Version 2.0 (the "License");
64306 * you may not use this file except in compliance with the License.
64307 * You may obtain a copy of the License at
64308 *
64309 * http://www.apache.org/licenses/LICENSE-2.0
64310 *
64311 * Unless required by applicable law or agreed to in writing, software
64312 * distributed under the License is distributed on an "AS IS" BASIS,
64313 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
64314 * See the License for the specific language governing permissions and
64315 * limitations under the License.
64316 */
64317
64318 namespace PhpCollection;
64319
64320 /**
64321 * Basic interface which adds some behaviors, and a few methods common to all collections.
64322 *
64323 * @author Johannes M. Schmitt <schmittjoh@gmail.com>
64324 */
64325 interface CollectionInterface extends \Traversable, \Countable
64326 {
64327 /**
64328 * Returns whether this collection contains the passed element.
64329 *
64330 * @param mixed $elem
64331 *
64332 * @return boolean
64333 */
64334 public function contains($elem);
64335
64336 /**
64337 * Returns whether the collection is empty.
64338 *
64339 * @return boolean
64340 */
64341 public function isEmpty();
64342 }<?php
64343
64344 /*
64345 * Copyright 2012 Johannes M. Schmitt <schmittjoh@gmail.com>
64346 *
64347 * Licensed under the Apache License, Version 2.0 (the "License");
64348 * you may not use this file except in compliance with the License.
64349 * You may obtain a copy of the License at
64350 *
64351 * http://www.apache.org/licenses/LICENSE-2.0
64352 *
64353 * Unless required by applicable law or agreed to in writing, software
64354 * distributed under the License is distributed on an "AS IS" BASIS,
64355 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
64356 * See the License for the specific language governing permissions and
64357 * limitations under the License.
64358 */
64359
64360 namespace PhpCollection;
64361
64362 /**
64363 * Interface for mutable sequences.
64364 *
64365 * Equality of elements in the sequence is established via a shallow comparison (===).
64366 *
64367 * @author Johannes M. Schmitt <schmittjoh@gmail.com>
64368 */
64369 interface SequenceInterface extends CollectionInterface
64370 {
64371 /**
64372 * Returns the first element in the collection if available.
64373 *
64374 * @return Option<T>
64375 */
64376 public function first();
64377
64378 /**
64379 * Returns the last element in the collection if available.
64380 *
64381 * @return Option<T>
64382 */
64383 public function last();
64384
64385 /**
64386 * Returns all elements in this sequence.
64387 *
64388 * @return array<T>
64389 */
64390 public function all();
64391
64392 /**
64393 * Adds the elements of another sequence to this sequence.
64394 *
64395 * @param SequenceInterface<T> $seq
64396 *
64397 * @return SequenceInterface<T>
64398 */
64399 public function addSequence(SequenceInterface $seq);
64400
64401 /**
64402 * Returns the index of the passed element.
64403 *
64404 * @param T $elem
64405 *
64406 * @return integer the index (0-based), or -1 if not found
64407 */
64408 public function indexOf($elem);
64409
64410 /**
64411 * Returns the last index of the passed element.
64412 *
64413 * @param T $elem
64414 * @return integer the index (0-based), or -1 if not found
64415 */
64416 public function lastIndexOf($elem);
64417
64418 /**
64419 * Returns whether the given index is defined in the sequence.
64420 *
64421 * @param integer $index (0-based)
64422 * @return boolean
64423 */
64424 public function isDefinedAt($index);
64425
64426 /**
64427 * Returns the first index where the given callable returns true.
64428 *
64429 * @param callable $callable receives the element as first argument, and returns true, or false
64430 *
64431 * @return integer the index (0-based), or -1 if the callable returns false for all elements
64432 */
64433 public function indexWhere($callable);
64434
64435 /**
64436 * Returns the last index where the given callable returns true.
64437 *
64438 * @param callable $callable receives the element as first argument, and returns true, or false
64439 *
64440 * @return integer the index (0-based), or -1 if the callable returns false for all elements
64441 */
64442 public function lastIndexWhere($callable);
64443
64444 /**
64445 * Returns all indices of this collection.
64446 *
64447 * @return array<integer>
64448 */
64449 public function indices();
64450
64451 /**
64452 * Returns the element at the given index.
64453 *
64454 * @param integer $index (0-based)
64455 *
64456 * @return T
64457 */
64458 public function get($index);
64459
64460 /**
64461 * Adds an element to the sequence.
64462 *
64463 * @param T $elem
64464 *
64465 * @return void
64466 */
64467 public function add($elem);
64468
64469 /**
64470 * Removes the element at the given index, and returns it.
64471 *
64472 * @param integer $index
64473 *
64474 * @return T
64475 */
64476 public function remove($index);
64477
64478 /**
64479 * Adds all elements to the sequence.
64480 *
64481 * @param array<T> $elements
64482 *
64483 * @return void
64484 */
64485 public function addAll(array $elements);
64486
64487 /**
64488 * Updates the value at the given index.
64489 *
64490 * @param integer $index
64491 * @param T $value
64492 *
64493 * @return void
64494 */
64495 public function update($index, $value);
64496
64497 /**
64498 * Returns a new sequence by omitting the given number of elements from the beginning.
64499 *
64500 * If the passed number is greater than the available number of elements, all will be removed.
64501 *
64502 * @param integer $number
64503 *
64504 * @return SequenceInterface
64505 */
64506 public function drop($number);
64507
64508 /**
64509 * Returns a new sequence by omitting the given number of elements from the end.
64510 *
64511 * If the passed number is greater than the available number of elements, all will be removed.
64512 *
64513 * @param integer $number
64514 *
64515 * @return SequenceInterface
64516 */
64517 public function dropRight($number);
64518
64519 /**
64520 * Returns a new sequence by omitting elements from the beginning for as long as the callable returns true.
64521 *
64522 * @param callable $callable Receives the element to drop as first argument, and returns true (drop), or false (stop).
64523 *
64524 * @return SequenceInterface
64525 */
64526 public function dropWhile($callable);
64527
64528 /**
64529 * Creates a new collection by taking the given number of elements from the beginning
64530 * of the current collection.
64531 *
64532 * If the passed number is greater than the available number of elements, then all elements
64533 * will be returned as a new collection.
64534 *
64535 * @param integer $number
64536 *
64537 * @return CollectionInterface<T>
64538 */
64539 public function take($number);
64540
64541 /**
64542 * Creates a new collection by taking elements from the current collection
64543 * for as long as the callable returns true.
64544 *
64545 * @param callable $callable
64546 *
64547 * @return CollectionInterface<T>
64548 */
64549 public function takeWhile($callable);
64550 }
64551 <?php
64552
64553 /*
64554 * Copyright 2012 Johannes M. Schmitt <schmittjoh@gmail.com>
64555 *
64556 * Licensed under the Apache License, Version 2.0 (the "License");
64557 * you may not use this file except in compliance with the License.
64558 * You may obtain a copy of the License at
64559 *
64560 * http://www.apache.org/licenses/LICENSE-2.0
64561 *
64562 * Unless required by applicable law or agreed to in writing, software
64563 * distributed under the License is distributed on an "AS IS" BASIS,
64564 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
64565 * See the License for the specific language governing permissions and
64566 * limitations under the License.
64567 */
64568
64569 namespace PhpCollection;
64570
64571 use PhpOption\Some;
64572 use PhpOption\None;
64573
64574 /**
64575 * A simple map implementation which basically wraps an array with an object oriented interface.
64576 *
64577 * @IgnoreAnnotation("template")
64578 * @template {K extends string} This implementation only supports strings as keys.
64579 *
64580 * @author Johannes M. Schmitt <schmittjoh@gmail.com>
64581 */
64582 class AbstractMap extends AbstractCollection implements \IteratorAggregate, MapInterface
64583 {
64584 protected $elements;
64585
64586 public function __construct(array $elements = array())
64587 {
64588 $this->elements = $elements;
64589 }
64590
64591 public function set($key, $value)
64592 {
64593 $this->elements[$key] = $value;
64594 }
64595
64596 /**
64597 * Sets all key/value pairs in the map.
64598 *
64599 * @param array<string,T> $kvMap
64600 *
64601 * @return void
64602 */
64603 public function setAll(array $kvMap)
64604 {
64605 $this->elements = array_merge($this->elements, $kvMap);
64606 }
64607
64608 public function addMap(MapInterface $map)
64609 {
64610 foreach ($map as $k => $v) {
64611 $this->elements[$k] = $v;
64612 }
64613 }
64614
64615 public function get($key)
64616 {
64617 if (isset($this->elements[$key])) {
64618 return new Some($this->elements[$key]);
64619 }
64620
64621 return None::create();
64622 }
64623
64624 public function remove($key)
64625 {
64626 if ( ! isset($this->elements[$key])) {
64627 throw new \InvalidArgumentException(sprintf('The map has no key named "%s".', $key));
64628 }
64629
64630 $element = $this->elements[$key];
64631 unset($this->elements[$key]);
64632
64633 return $element;
64634 }
64635
64636 public function clear()
64637 {
64638 $this->elements = array();
64639 }
64640
64641 public function first()
64642 {
64643 if (empty($this->elements)) {
64644 return None::create();
64645 }
64646
64647 $elem = reset($this->elements);
64648
64649 return new Some(array(key($this->elements), $elem));
64650 }
64651
64652 public function last()
64653 {
64654 if (empty($this->elements)) {
64655 return None::create();
64656 }
64657
64658 $elem = end($this->elements);
64659
64660 return new Some(array(key($this->elements), $elem));
64661 }
64662
64663 public function contains($elem)
64664 {
64665 foreach ($this->elements as $existingElem) {
64666 if ($existingElem === $elem) {
64667 return true;
64668 }
64669 }
64670
64671 return false;
64672 }
64673
64674 public function containsKey($key)
64675 {
64676 return isset($this->elements[$key]);
64677 }
64678
64679 public function isEmpty()
64680 {
64681 return empty($this->elements);
64682 }
64683
64684 public function dropWhile($callable)
64685 {
64686 $newElements = array();
64687 $stopped = false;
64688 foreach ($this->elements as $k => $v) {
64689 if ( ! $stopped) {
64690 if (call_user_func($callable, $k, $v) === true) {
64691 continue;
64692 }
64693
64694 $stopped = true;
64695 }
64696
64697 $newElements[$k] = $v;
64698 }
64699
64700 return $this->createNew($newElements);
64701 }
64702
64703 public function drop($number)
64704 {
64705 if ($number <= 0) {
64706 throw new \InvalidArgumentException(sprintf('The number must be greater than 0, but got %d.', $number));
64707 }
64708
64709 return $this->createNew(array_slice($this->elements, $number, null, true));
64710 }
64711
64712 public function dropRight($number)
64713 {
64714 if ($number <= 0) {
64715 throw new \InvalidArgumentException(sprintf('The number must be greater than 0, but got %d.', $number));
64716 }
64717
64718 return $this->createNew(array_slice($this->elements, 0, -1 * $number, true));
64719 }
64720
64721 public function take($number)
64722 {
64723 if ($number <= 0) {
64724 throw new \InvalidArgumentException(sprintf('The number must be greater than 0, but got %d.', $number));
64725 }
64726
64727 return $this->createNew(array_slice($this->elements, 0, $number, true));
64728 }
64729
64730 public function takeWhile($callable)
64731 {
64732 $newElements = array();
64733 foreach ($this->elements as $k => $v) {
64734 if (call_user_func($callable, $k, $v) !== true) {
64735 break;
64736 }
64737
64738 $newElements[$k] = $v;
64739 }
64740
64741 return $this->createNew($newElements);
64742 }
64743
64744 public function find($callable)
64745 {
64746 foreach ($this->elements as $k => $v) {
64747 if (call_user_func($callable, $k, $v) === true) {
64748 return new Some(array($k, $v));
64749 }
64750 }
64751
64752 return None::create();
64753 }
64754
64755 public function keys()
64756 {
64757 return array_keys($this->elements);
64758 }
64759
64760 public function values()
64761 {
64762 return array_values($this->elements);
64763 }
64764
64765 public function count()
64766 {
64767 return count($this->elements);
64768 }
64769
64770 public function getIterator()
64771 {
64772 return new \ArrayIterator($this->elements);
64773 }
64774
64775 protected function createNew(array $elements)
64776 {
64777 return new static($elements);
64778 }
64779 }<?php
64780
64781 /*
64782 * Copyright 2012 Johannes M. Schmitt <schmittjoh@gmail.com>
64783 *
64784 * Licensed under the Apache License, Version 2.0 (the "License");
64785 * you may not use this file except in compliance with the License.
64786 * You may obtain a copy of the License at
64787 *
64788 * http://www.apache.org/licenses/LICENSE-2.0
64789 *
64790 * Unless required by applicable law or agreed to in writing, software
64791 * distributed under the License is distributed on an "AS IS" BASIS,
64792 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
64793 * See the License for the specific language governing permissions and
64794 * limitations under the License.
64795 */
64796
64797 namespace PhpCollection;
64798
64799 use PhpOption\Some;
64800 use PhpOption\None;
64801 use PhpOption\Option;
64802 use OutOfBoundsException;
64803
64804 /**
64805 * A sequence with numerically indexed elements.
64806 *
64807 * This is rawly equivalent to an array with only numeric keys.
64808 * There are no restrictions on how many same values may occur in the sequence.
64809 *
64810 * This sequence is mutable.
64811 *
64812 * @IgnoreAnnotation("template")
64813 * @template T The type that this sequence contains.
64814 *
64815 * @author Johannes M. Schmitt <schmittjoh@gmail.com>
64816 */
64817 class AbstractSequence extends AbstractCollection implements \IteratorAggregate, SequenceInterface
64818 {
64819 protected $elements;
64820
64821 /**
64822 * @param array<T> $elements
64823 */
64824 public function __construct(array $elements = array())
64825 {
64826 $this->elements = array_values($elements);
64827 }
64828
64829 public function addSequence(SequenceInterface $seq)
64830 {
64831 $this->addAll($seq->all());
64832 }
64833
64834 public function indexOf($searchedElement)
64835 {
64836 foreach ($this->elements as $i => $element) {
64837 if ($searchedElement === $element) {
64838 return $i;
64839 }
64840 }
64841
64842 return -1;
64843 }
64844
64845 public function lastIndexOf($searchedElement)
64846 {
64847 for ($i=count($this->elements)-1; $i>=0; $i--) {
64848 if ($this->elements[$i] === $searchedElement) {
64849 return $i;
64850 }
64851 }
64852
64853 return -1;
64854 }
64855
64856 public function isDefinedAt($index)
64857 {
64858 return isset($this->elements[$index]);
64859 }
64860
64861 /**
64862 * Finds the first index where the given callable returns true.
64863 *
64864 * @param callable $callable
64865 *
64866 * @return integer the index, or -1 if the predicate is not true for any element.
64867 */
64868 public function indexWhere($callable)
64869 {
64870 foreach ($this->elements as $i => $element) {
64871 if (call_user_func($callable, $element) === true) {
64872 return $i;
64873 }
64874 }
64875
64876 return -1;
64877 }
64878
64879 public function lastIndexWhere($callable)
64880 {
64881 for ($i=count($this->elements)-1; $i>=0; $i--) {
64882 if (call_user_func($callable, $this->elements[$i]) === true) {
64883 return $i;
64884 }
64885 }
64886
64887 return -1;
64888 }
64889
64890 public function last()
64891 {
64892 if (empty($this->elements)) {
64893 return None::create();
64894 }
64895
64896 return new Some(end($this->elements));
64897 }
64898
64899 public function first()
64900 {
64901 if (empty($this->elements)) {
64902 return None::create();
64903 }
64904
64905 return new Some(reset($this->elements));
64906 }
64907
64908 public function indices()
64909 {
64910 return array_keys($this->elements);
64911 }
64912
64913 /**
64914 * Returns an element based on its index (0-based).
64915 *
64916 * @param integer $index
64917 *
64918 * @return T
64919 */
64920 public function get($index)
64921 {
64922 if ( ! isset($this->elements[$index])) {
64923 throw new OutOfBoundsException(sprintf('The index "%s" does not exist in this sequence.', $index));
64924 }
64925
64926 return $this->elements[$index];
64927 }
64928
64929 /**
64930 * Removes the element at the given index, and returns it.
64931 *
64932 * @param int $index
64933 *
64934 * @return T
64935 *
64936 * @throws \OutOfBoundsException If there is no element at the given index.
64937 */
64938 public function remove($index)
64939 {
64940 if ( ! isset($this->elements[$index])) {
64941 throw new OutOfBoundsException(sprintf('The index "%d" is not in the interval [0, %d).', $index, count($this->elements)));
64942 }
64943
64944 $element = $this->elements[$index];
64945 unset($this->elements[$index]);
64946 $this->elements = array_values($this->elements);
64947
64948 return $element;
64949 }
64950
64951 /**
64952 * Updates the element at the given index (0-based).
64953 *
64954 * @param integer $index
64955 * @param T $value
64956 */
64957 public function update($index, $value)
64958 {
64959 if ( ! isset($this->elements[$index])) {
64960 throw new \InvalidArgumentException(sprintf('There is no element at index "%d".', $index));
64961 }
64962
64963 $this->elements[$index] = $value;
64964 }
64965
64966 public function isEmpty()
64967 {
64968 return empty($this->elements);
64969 }
64970
64971 public function all()
64972 {
64973 return $this->elements;
64974 }
64975
64976 public function add($newElement)
64977 {
64978 $this->elements[] = $newElement;
64979 }
64980
64981 public function addAll(array $addedElements)
64982 {
64983 foreach ($addedElements as $newElement) {
64984 $this->elements[] = $newElement;
64985 }
64986 }
64987
64988 public function take($number)
64989 {
64990 if ($number <= 0) {
64991 throw new \InvalidArgumentException(sprintf('$number must be greater than 0, but got %d.', $number));
64992 }
64993
64994 return $this->createNew(array_slice($this->elements, 0, $number));
64995 }
64996
64997 /**
64998 * Extracts element from the head while the passed callable returns true.
64999 *
65000 * @param callable $callable receives elements of this sequence as first argument, and returns true/false.
65001 *
65002 * @return Sequence<T>
65003 */
65004 public function takeWhile($callable)
65005 {
65006 $newElements = array();
65007
65008 for ($i=0,$c=count($this->elements); $i<$c; $i++) {
65009 if (call_user_func($callable, $this->elements[$i]) !== true) {
65010 break;
65011 }
65012
65013 $newElements[] = $this->elements[$i];
65014 }
65015
65016 return $this->createNew($newElements);
65017 }
65018
65019 public function drop($number)
65020 {
65021 if ($number <= 0) {
65022 throw new \InvalidArgumentException(sprintf('The number must be greater than 0, but got %d.', $number));
65023 }
65024
65025 return $this->createNew(array_slice($this->elements, $number));
65026 }
65027
65028 public function dropRight($number)
65029 {
65030 if ($number <= 0) {
65031 throw new \InvalidArgumentException(sprintf('The number must be greater than 0, but got %d.', $number));
65032 }
65033
65034 return $this->createNew(array_slice($this->elements, 0, -1 * $number));
65035 }
65036
65037 public function dropWhile($callable)
65038 {
65039 for ($i=0,$c=count($this->elements); $i<$c; $i++) {
65040 if (true !== call_user_func($callable, $this->elements[$i])) {
65041 break;
65042 }
65043 }
65044
65045 return $this->createNew(array_slice($this->elements, $i));
65046 }
65047
65048 public function count()
65049 {
65050 return count($this->elements);
65051 }
65052
65053 public function getIterator()
65054 {
65055 return new \ArrayIterator($this->elements);
65056 }
65057
65058 protected function createNew(array $elements)
65059 {
65060 return new static($elements);
65061 }
65062 }<?php
65063
65064 /*
65065 * Copyright 2012 Johannes M. Schmitt <schmittjoh@gmail.com>
65066 *
65067 * Licensed under the Apache License, Version 2.0 (the "License");
65068 * you may not use this file except in compliance with the License.
65069 * You may obtain a copy of the License at
65070 *
65071 * http://www.apache.org/licenses/LICENSE-2.0
65072 *
65073 * Unless required by applicable law or agreed to in writing, software
65074 * distributed under the License is distributed on an "AS IS" BASIS,
65075 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
65076 * See the License for the specific language governing permissions and
65077 * limitations under the License.
65078 */
65079
65080 namespace PhpCollection;
65081
65082 /**
65083 * Unsorted sequence implementation.
65084 *
65085 * Characteristics:
65086 *
65087 * - Keys: consequentially numbered, without gaps
65088 * - Values: anything, duplicates allowed
65089 * - Ordering: same as input unless when explicitly sorted
65090 *
65091 * @author Johannes M. Schmitt <schmittjoh@gmail.com>
65092 */
65093 class Sequence extends AbstractSequence implements SortableInterface
65094 {
65095 public function sortWith($callable)
65096 {
65097 usort($this->elements, $callable);
65098 }
65099 }<?php
65100
65101 /*
65102 * Copyright 2012 Johannes M. Schmitt <schmittjoh@gmail.com>
65103 *
65104 * Licensed under the Apache License, Version 2.0 (the "License");
65105 * you may not use this file except in compliance with the License.
65106 * You may obtain a copy of the License at
65107 *
65108 * http://www.apache.org/licenses/LICENSE-2.0
65109 *
65110 * Unless required by applicable law or agreed to in writing, software
65111 * distributed under the License is distributed on an "AS IS" BASIS,
65112 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
65113 * See the License for the specific language governing permissions and
65114 * limitations under the License.
65115 */
65116
65117 namespace PhpCollection;
65118
65119 /**
65120 * Interface for sortable collections.
65121 *
65122 * @author Johannes M. Schmitt <schmittjoh@gmail.com>
65123 */
65124 interface SortableInterface
65125 {
65126 public function sortWith($callable);
65127 }<?php
65128
65129 /*
65130 * Copyright 2012 Johannes M. Schmitt <schmittjoh@gmail.com>
65131 *
65132 * Licensed under the Apache License, Version 2.0 (the "License");
65133 * you may not use this file except in compliance with the License.
65134 * You may obtain a copy of the License at
65135 *
65136 * http://www.apache.org/licenses/LICENSE-2.0
65137 *
65138 * Unless required by applicable law or agreed to in writing, software
65139 * distributed under the License is distributed on an "AS IS" BASIS,
65140 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
65141 * See the License for the specific language governing permissions and
65142 * limitations under the License.
65143 */
65144
65145 namespace PhpCollection;
65146
65147 /**
65148 * Basic map interface.
65149 *
65150 * @IgnoreAnnotation("template")
65151 * @template K the type of the keys
65152 * @template V the type of the values
65153 *
65154 * @author Johannes M. Schmitt <schmittjoh@gmail.com>
65155 */
65156 interface MapInterface extends CollectionInterface
65157 {
65158 /**
65159 * Returns the first element in the collection if available.
65160 *
65161 * @return Option<array<K,V>>
65162 */
65163 public function first();
65164
65165 /**
65166 * Returns the last element in the collection if available.
65167 *
65168 * @return Option<array<K,V>>
65169 */
65170 public function last();
65171
65172 /**
65173 * Searches the collection for an element.
65174 *
65175 * @param callable $callable receives the element as first argument, and returns true, or false
65176 *
65177 * @return Option<array<K,V>>
65178 */
65179 public function find($callable);
65180
65181 /**
65182 * Returns the value associated with the given key.
65183 *
65184 * @param K $key
65185 *
65186 * @return Option<V>
65187 */
65188 public function get($key);
65189
65190 /**
65191 * Returns whether this map contains a given key.
65192 *
65193 * @param K $key
65194 *
65195 * @return boolean
65196 */
65197 public function containsKey($key);
65198
65199 /**
65200 * Puts a new element in the map.
65201 *
65202 * @param K $key
65203 * @param V $value
65204 *
65205 * @return void
65206 */
65207 public function set($key, $value);
65208
65209 /**
65210 * Removes an element from the map.
65211 *
65212 * @param K $key
65213 *
65214 * @return V
65215 */
65216 public function remove($key);
65217
65218 /**
65219 * Adds all another map to this map, and returns itself.
65220 *
65221 * @param MapInterface<K,V> $map
65222 *
65223 * @return MapInterface<K,V>
65224 */
65225 public function addMap(MapInterface $map);
65226
65227 /**
65228 * Returns an array with the keys.
65229 *
65230 * @return array<K>
65231 */
65232 public function keys();
65233
65234 /**
65235 * Returns an array with the values.
65236 *
65237 * @return array<V>
65238 */
65239 public function values();
65240
65241 /**
65242 * Returns a new sequence by omitting the given number of elements from the beginning.
65243 *
65244 * If the passed number is greater than the available number of elements, all will be removed.
65245 *
65246 * @param integer $number
65247 *
65248 * @return MapInterface<K,V>
65249 */
65250 public function drop($number);
65251
65252 /**
65253 * Returns a new sequence by omitting the given number of elements from the end.
65254 *
65255 * If the passed number is greater than the available number of elements, all will be removed.
65256 *
65257 * @param integer $number
65258 *
65259 * @return MapInterface<K,V>
65260 */
65261 public function dropRight($number);
65262
65263 /**
65264 * Returns a new sequence by omitting elements from the beginning for as long as the callable returns true.
65265 *
65266 * @param callable $callable Receives the element to drop as first argument, and returns true (drop), or false (stop).
65267 *
65268 * @return MapInterface<K,V>
65269 */
65270 public function dropWhile($callable);
65271
65272 /**
65273 * Creates a new collection by taking the given number of elements from the beginning
65274 * of the current collection.
65275 *
65276 * If the passed number is greater than the available number of elements, then all elements
65277 * will be returned as a new collection.
65278 *
65279 * @param integer $number
65280 *
65281 * @return MapInterface<K,V>
65282 */
65283 public function take($number);
65284
65285 /**
65286 * Creates a new collection by taking elements from the current collection
65287 * for as long as the callable returns true.
65288 *
65289 * @param callable $callable
65290 *
65291 * @return MapInterface<K,V>
65292 */
65293 public function takeWhile($callable);
65294 }
65295 <?php
65296
65297 /*
65298 * Copyright 2012 Johannes M. Schmitt <schmittjoh@gmail.com>
65299 *
65300 * Licensed under the Apache License, Version 2.0 (the "License");
65301 * you may not use this file except in compliance with the License.
65302 * You may obtain a copy of the License at
65303 *
65304 * http://www.apache.org/licenses/LICENSE-2.0
65305 *
65306 * Unless required by applicable law or agreed to in writing, software
65307 * distributed under the License is distributed on an "AS IS" BASIS,
65308 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
65309 * See the License for the specific language governing permissions and
65310 * limitations under the License.
65311 */
65312
65313 namespace PhpCollection;
65314
65315 /**
65316 * A sequence with a fixed sort-order.
65317 *
65318 * @author Johannes M. Schmitt <schmittjoh@gmail.com>
65319 */
65320 class SortedSequence extends AbstractSequence
65321 {
65322 private $sortFunc;
65323
65324 public function __construct($sortFunc, array $elements = array())
65325 {
65326 usort($elements, $sortFunc);
65327 parent::__construct($elements);
65328
65329 $this->sortFunc = $sortFunc;
65330 }
65331
65332 public function add($newElement)
65333 {
65334 $added = false;
65335 $newElements = array();
65336 foreach ($this->elements as $element) {
65337 // We insert the new element before the first element that is greater than itself.
65338 if ( ! $added && (integer) call_user_func($this->sortFunc, $newElement, $element) < 0) {
65339 $newElements[] = $newElement;
65340 $added = true;
65341 }
65342
65343 $newElements[] = $element;
65344 }
65345
65346 if ( ! $added) {
65347 $newElements[] = $newElement;
65348 }
65349 $this->elements = $newElements;
65350 }
65351
65352 public function addAll(array $addedElements)
65353 {
65354 usort($addedElements, $this->sortFunc);
65355
65356 $newElements = array();
65357 foreach ($this->elements as $element) {
65358 if ( ! empty($addedElements)) {
65359 foreach ($addedElements as $i => $newElement) {
65360 // If the currently looked at $newElement is not smaller than $element, then we can also conclude
65361 // that all other new elements are also not smaller than $element as we have ordered them before.
65362 if ((integer) call_user_func($this->sortFunc, $newElement, $element) > -1) {
65363 break;
65364 }
65365
65366 $newElements[] = $newElement;
65367 unset($addedElements[$i]);
65368 }
65369 }
65370
65371 $newElements[] = $element;
65372 }
65373
65374 if ( ! empty($addedElements)) {
65375 foreach ($addedElements as $newElement) {
65376 $newElements[] = $newElement;
65377 }
65378 }
65379
65380 $this->elements = $newElements;
65381 }
65382
65383 protected function createNew(array $elements)
65384 {
65385 return new static($this->sortFunc, $elements);
65386 }
65387 }{
65388 "hash": "02937754d829ce8d6ca657debd7324ad",
65389 "packages": [
65390 {
65391 "name": "phpoption/phpoption",
65392 "version": "0.9.0",
65393 "source": {
65394 "type": "git",
65395 "url": "git://github.com/schmittjoh/php-option",
65396 "reference": "0.9.0"
65397 },
65398 "dist": {
65399 "type": "zip",
65400 "url": "https://github.com/schmittjoh/php-option/archive/0.9.0.zip",
65401 "reference": "0.9.0",
65402 "shasum": ""
65403 },
65404 "require": {
65405 "php": ">=5.3.0"
65406 },
65407 "require-dev": {
65408 "phpunit/phpunit": "3.7.*"
65409 },
65410 "time": "2012-11-12 08:25:35",
65411 "type": "library",
65412 "installation-source": "dist",
65413 "autoload": {
65414 "psr-0": {
65415 "PhpOption\\": "src/"
65416 }
65417 },
65418 "license": [
65419 "Apache2"
65420 ],
65421 "authors": [
65422 {
65423 "name": "Johannes M. Schmitt",
65424 "email": "schmittjoh@gmail.com",
65425 "homepage": "https://github.com/schmittjoh",
65426 "role": "Developer of wrapped JMSSerializerBundle"
65427 }
65428 ],
65429 "description": "Option Type for PHP",
65430 "keywords": [
65431 "php",
65432 "language",
65433 "type",
65434 "option"
65435 ]
65436 }
65437 ],
65438 "packages-dev": [
65439
65440 ],
65441 "aliases": [
65442
65443 ],
65444 "minimum-stability": "stable",
65445 "stability-flags": [
65446
65447 ]
65448 }
65449 THE WORK (AS DEFINED BELOW) IS PROVIDED UNDER THE TERMS OF THIS CREATIVE COMMONS PUBLIC LICENSE ("CCPL" OR "LICENSE"). THE WORK IS PROTECTED BY COPYRIGHT AND/OR OTHER APPLICABLE LAW. ANY USE OF THE WORK OTHER THAN AS AUTHORIZED UNDER THIS LICENSE OR COPYRIGHT LAW IS PROHIBITED.
65450
65451 BY EXERCISING ANY RIGHTS TO THE WORK PROVIDED HERE, YOU ACCEPT AND AGREE TO BE BOUND BY THE TERMS OF THIS LICENSE. TO THE EXTENT THIS LICENSE MAY BE CONSIDERED TO BE A CONTRACT, THE LICENSOR GRANTS YOU THE RIGHTS CONTAINED HERE IN CONSIDERATION OF YOUR ACCEPTANCE OF SUCH TERMS AND CONDITIONS.
65452
65453 1. Definitions
65454
65455 "Adaptation" means a work based upon the Work, or upon the Work and other pre-existing works, such as a translation, adaptation, derivative work, arrangement of music or other alterations of a literary or artistic work, or phonogram or performance and includes cinematographic adaptations or any other form in which the Work may be recast, transformed, or adapted including in any form recognizably derived from the original, except that a work that constitutes a Collection will not be considered an Adaptation for the purpose of this License. For the avoidance of doubt, where the Work is a musical work, performance or phonogram, the synchronization of the Work in timed-relation with a moving image ("synching") will be considered an Adaptation for the purpose of this License.
65456 "Collection" means a collection of literary or artistic works, such as encyclopedias and anthologies, or performances, phonograms or broadcasts, or other works or subject matter other than works listed in Section 1(f) below, which, by reason of the selection and arrangement of their contents, constitute intellectual creations, in which the Work is included in its entirety in unmodified form along with one or more other contributions, each constituting separate and independent works in themselves, which together are assembled into a collective whole. A work that constitutes a Collection will not be considered an Adaptation (as defined above) for the purposes of this License.
65457 "Distribute" means to make available to the public the original and copies of the Work through sale or other transfer of ownership.
65458 "Licensor" means the individual, individuals, entity or entities that offer(s) the Work under the terms of this License.
65459 "Original Author" means, in the case of a literary or artistic work, the individual, individuals, entity or entities who created the Work or if no individual or entity can be identified, the publisher; and in addition (i) in the case of a performance the actors, singers, musicians, dancers, and other persons who act, sing, deliver, declaim, play in, interpret or otherwise perform literary or artistic works or expressions of folklore; (ii) in the case of a phonogram the producer being the person or legal entity who first fixes the sounds of a performance or other sounds; and, (iii) in the case of broadcasts, the organization that transmits the broadcast.
65460 "Work" means the literary and/or artistic work offered under the terms of this License including without limitation any production in the literary, scientific and artistic domain, whatever may be the mode or form of its expression including digital form, such as a book, pamphlet and other writing; a lecture, address, sermon or other work of the same nature; a dramatic or dramatico-musical work; a choreographic work or entertainment in dumb show; a musical composition with or without words; a cinematographic work to which are assimilated works expressed by a process analogous to cinematography; a work of drawing, painting, architecture, sculpture, engraving or lithography; a photographic work to which are assimilated works expressed by a process analogous to photography; a work of applied art; an illustration, map, plan, sketch or three-dimensional work relative to geography, topography, architecture or science; a performance; a broadcast; a phonogram; a compilation of data to the extent it is protected as a copyrightable work; or a work performed by a variety or circus performer to the extent it is not otherwise considered a literary or artistic work.
65461 "You" means an individual or entity exercising rights under this License who has not previously violated the terms of this License with respect to the Work, or who has received express permission from the Licensor to exercise rights under this License despite a previous violation.
65462 "Publicly Perform" means to perform public recitations of the Work and to communicate to the public those public recitations, by any means or process, including by wire or wireless means or public digital performances; to make available to the public Works in such a way that members of the public may access these Works from a place and at a place individually chosen by them; to perform the Work to the public by any means or process and the communication to the public of the performances of the Work, including by public digital performance; to broadcast and rebroadcast the Work by any means including signs, sounds or images.
65463 "Reproduce" means to make copies of the Work by any means including without limitation by sound or visual recordings and the right of fixation and reproducing fixations of the Work, including storage of a protected performance or phonogram in digital form or other electronic medium.
65464
65465 2. Fair Dealing Rights. Nothing in this License is intended to reduce, limit, or restrict any uses free from copyright or rights arising from limitations or exceptions that are provided for in connection with the copyright protection under copyright law or other applicable laws.
65466
65467 3. License Grant. Subject to the terms and conditions of this License, Licensor hereby grants You a worldwide, royalty-free, non-exclusive, perpetual (for the duration of the applicable copyright) license to exercise the rights in the Work as stated below:
65468
65469 to Reproduce the Work, to incorporate the Work into one or more Collections, and to Reproduce the Work as incorporated in the Collections; and,
65470 to Distribute and Publicly Perform the Work including as incorporated in Collections.
65471
65472 The above rights may be exercised in all media and formats whether now known or hereafter devised. The above rights include the right to make such modifications as are technically necessary to exercise the rights in other media and formats, but otherwise you have no rights to make Adaptations. Subject to 8(f), all rights not expressly granted by Licensor are hereby reserved, including but not limited to the rights set forth in Section 4(d).
65473
65474 4. Restrictions. The license granted in Section 3 above is expressly made subject to and limited by the following restrictions:
65475
65476 You may Distribute or Publicly Perform the Work only under the terms of this License. You must include a copy of, or the Uniform Resource Identifier (URI) for, this License with every copy of the Work You Distribute or Publicly Perform. You may not offer or impose any terms on the Work that restrict the terms of this License or the ability of the recipient of the Work to exercise the rights granted to that recipient under the terms of the License. You may not sublicense the Work. You must keep intact all notices that refer to this License and to the disclaimer of warranties with every copy of the Work You Distribute or Publicly Perform. When You Distribute or Publicly Perform the Work, You may not impose any effective technological measures on the Work that restrict the ability of a recipient of the Work from You to exercise the rights granted to that recipient under the terms of the License. This Section 4(a) applies to the Work as incorporated in a Collection, but this does not require the Collection apart from the Work itself to be made subject to the terms of this License. If You create a Collection, upon notice from any Licensor You must, to the extent practicable, remove from the Collection any credit as required by Section 4(c), as requested.
65477 You may not exercise any of the rights granted to You in Section 3 above in any manner that is primarily intended for or directed toward commercial advantage or private monetary compensation. The exchange of the Work for other copyrighted works by means of digital file-sharing or otherwise shall not be considered to be intended for or directed toward commercial advantage or private monetary compensation, provided there is no payment of any monetary compensation in connection with the exchange of copyrighted works.
65478 If You Distribute, or Publicly Perform the Work or Collections, You must, unless a request has been made pursuant to Section 4(a), keep intact all copyright notices for the Work and provide, reasonable to the medium or means You are utilizing: (i) the name of the Original Author (or pseudonym, if applicable) if supplied, and/or if the Original Author and/or Licensor designate another party or parties (e.g., a sponsor institute, publishing entity, journal) for attribution ("Attribution Parties") in Licensor's copyright notice, terms of service or by other reasonable means, the name of such party or parties; (ii) the title of the Work if supplied; (iii) to the extent reasonably practicable, the URI, if any, that Licensor specifies to be associated with the Work, unless such URI does not refer to the copyright notice or licensing information for the Work. The credit required by this Section 4(c) may be implemented in any reasonable manner; provided, however, that in the case of a Collection, at a minimum such credit will appear, if a credit for all contributing authors of Collection appears, then as part of these credits and in a manner at least as prominent as the credits for the other contributing authors. For the avoidance of doubt, You may only use the credit required by this Section for the purpose of attribution in the manner set out above and, by exercising Your rights under this License, You may not implicitly or explicitly assert or imply any connection with, sponsorship or endorsement by the Original Author, Licensor and/or Attribution Parties, as appropriate, of You or Your use of the Work, without the separate, express prior written permission of the Original Author, Licensor and/or Attribution Parties.
65479
65480 For the avoidance of doubt:
65481 Non-waivable Compulsory License Schemes. In those jurisdictions in which the right to collect royalties through any statutory or compulsory licensing scheme cannot be waived, the Licensor reserves the exclusive right to collect such royalties for any exercise by You of the rights granted under this License;
65482 Waivable Compulsory License Schemes. In those jurisdictions in which the right to collect royalties through any statutory or compulsory licensing scheme can be waived, the Licensor reserves the exclusive right to collect such royalties for any exercise by You of the rights granted under this License if Your exercise of such rights is for a purpose or use which is otherwise than noncommercial as permitted under Section 4(b) and otherwise waives the right to collect royalties through any statutory or compulsory licensing scheme; and,
65483 Voluntary License Schemes. The Licensor reserves the right to collect royalties, whether individually or, in the event that the Licensor is a member of a collecting society that administers voluntary licensing schemes, via that society, from any exercise by You of the rights granted under this License that is for a purpose or use which is otherwise than noncommercial as permitted under Section 4(b).
65484 Except as otherwise agreed in writing by the Licensor or as may be otherwise permitted by applicable law, if You Reproduce, Distribute or Publicly Perform the Work either by itself or as part of any Collections, You must not distort, mutilate, modify or take other derogatory action in relation to the Work which would be prejudicial to the Original Author's honor or reputation.
65485
65486 5. Representations, Warranties and Disclaimer
65487
65488 UNLESS OTHERWISE MUTUALLY AGREED BY THE PARTIES IN WRITING, LICENSOR OFFERS THE WORK AS-IS AND MAKES NO REPRESENTATIONS OR WARRANTIES OF ANY KIND CONCERNING THE WORK, EXPRESS, IMPLIED, STATUTORY OR OTHERWISE, INCLUDING, WITHOUT LIMITATION, WARRANTIES OF TITLE, MERCHANTIBILITY, FITNESS FOR A PARTICULAR PURPOSE, NONINFRINGEMENT, OR THE ABSENCE OF LATENT OR OTHER DEFECTS, ACCURACY, OR THE PRESENCE OF ABSENCE OF ERRORS, WHETHER OR NOT DISCOVERABLE. SOME JURISDICTIONS DO NOT ALLOW THE EXCLUSION OF IMPLIED WARRANTIES, SO SUCH EXCLUSION MAY NOT APPLY TO YOU.
65489
65490 6. Limitation on Liability. EXCEPT TO THE EXTENT REQUIRED BY APPLICABLE LAW, IN NO EVENT WILL LICENSOR BE LIABLE TO YOU ON ANY LEGAL THEORY FOR ANY SPECIAL, INCIDENTAL, CONSEQUENTIAL, PUNITIVE OR EXEMPLARY DAMAGES ARISING OUT OF THIS LICENSE OR THE USE OF THE WORK, EVEN IF LICENSOR HAS BEEN ADVISED OF THE POSSIBILITY OF SUCH DAMAGES.
65491
65492 7. Termination
65493
65494 This License and the rights granted hereunder will terminate automatically upon any breach by You of the terms of this License. Individuals or entities who have received Collections from You under this License, however, will not have their licenses terminated provided such individuals or entities remain in full compliance with those licenses. Sections 1, 2, 5, 6, 7, and 8 will survive any termination of this License.
65495 Subject to the above terms and conditions, the license granted here is perpetual (for the duration of the applicable copyright in the Work). Notwithstanding the above, Licensor reserves the right to release the Work under different license terms or to stop distributing the Work at any time; provided, however that any such election will not serve to withdraw this License (or any other license that has been, or is required to be, granted under the terms of this License), and this License will continue in full force and effect unless terminated as stated above.
65496
65497 8. Miscellaneous
65498
65499 Each time You Distribute or Publicly Perform the Work or a Collection, the Licensor offers to the recipient a license to the Work on the same terms and conditions as the license granted to You under this License.
65500 If any provision of this License is invalid or unenforceable under applicable law, it shall not affect the validity or enforceability of the remainder of the terms of this License, and without further action by the parties to this agreement, such provision shall be reformed to the minimum extent necessary to make such provision valid and enforceable.
65501 No term or provision of this License shall be deemed waived and no breach consented to unless such waiver or consent shall be in writing and signed by the party to be charged with such waiver or consent.
65502 This License constitutes the entire agreement between the parties with respect to the Work licensed here. There are no understandings, agreements or representations with respect to the Work not specified here. Licensor shall not be bound by any additional provisions that may appear in any communication from You. This License may not be modified without the mutual written agreement of the Licensor and You.
65503 The rights granted under, and the subject matter referenced, in this License were drafted utilizing the terminology of the Berne Convention for the Protection of Literary and Artistic Works (as amended on September 28, 1979), the Rome Convention of 1961, the WIPO Copyright Treaty of 1996, the WIPO Performances and Phonograms Treaty of 1996 and the Universal Copyright Convention (as revised on July 24, 1971). These rights and subject matter take effect in the relevant jurisdiction in which the License terms are sought to be enforced according to the corresponding provisions of the implementation of those treaty provisions in the applicable national law. If the standard suite of rights granted under applicable copyright law includes additional rights not granted under this License, such additional rights are deemed to be included in the License; this License is not intended to restrict the license of any rights under applicable law.
65504 PHP Collection
65505 ==============
65506 This library adds basic collections for PHP.
65507
65508 Collections can be seen as more specialized arrays for which certain contracts are guaranteed.
65509
65510 Supported Collections:
65511
65512 - Sequences
65513
65514 - Keys: numerical, consequentially increasing, no gaps
65515 - Values: anything, duplicates allowed
65516 - Classes: ``Sequence``, ``SortedSequence``
65517
65518
65519 - Maps
65520
65521 - Keys: strings or objects, duplicate keys not allowed
65522 - Values: anything, duplicates allowed
65523 - Classes: ``Map``, ``ObjectMap`` (not yet implemented)
65524
65525
65526 - Sets (not yet implemented)
65527
65528 - Keys: not meaningful
65529 - Values: anything, each value must be unique (===)
65530 - Classes: ``Set``
65531
65532 General Characteristics:
65533
65534 - Collections are mutable (new elements may be added, existing elements may be modified or removed). Specialized
65535 immutable versions may be added in the future though.
65536 - Equality comparison between elements are always performed using the shallow comparison operator (===).
65537 - Sorting algorithms are unstable, that means the order for equal elements is undefined (the default, and only PHP behavior).
65538
65539
65540 Installation
65541 ------------
65542 PHP Collection can easily be installed via composer
65543
65544 .. code-block :: bash
65545
65546 composer require phpcollection/phpcollection
65547
65548 or add it to your ``composer.json`` file.
65549
65550 Usage
65551 -----
65552 Collection classes provide a rich API.
65553
65554 Sequences
65555 ~~~~~~~~~
65556
65557 .. code-block :: php
65558
65559 // Read Operations
65560 $seq = new Sequence([0, 2, 3, 2]);
65561 $seq->get(2); // int(2)
65562 $seq->all(); // [0, 2, 3, 2]
65563
65564 $seq->first(); // Some(0)
65565 $seq->last(); // Some(2)
65566
65567 // Write Operations
65568 $seq = new Sequence([1, 5]);
65569 $seq->get(0); // int(1)
65570 $seq->update(0, 4);
65571 $seq->get(0); // int(4)
65572 $seq->remove(0);
65573 $seq->get(0); // int(5)
65574
65575 $seq = new Sequence([1, 4]);
65576 $seq->add(2);
65577 $seq->all(); // [1, 4, 2]
65578 $seq->addAll(array(4, 5, 2));
65579 $seq->all(); // [1, 4, 2, 4, 5, 2]
65580
65581 // Sort
65582 $seq = new Sequence([0, 5, 4, 2]);
65583 $seq->sortWith(function($a, $b) { return $a - $b; });
65584 $seq->all(); // [0, 2, 4, 5]
65585
65586 Maps
65587 ~~~~
65588
65589 .. code-block :: php
65590
65591 // Read Operations
65592 $map = new Map(['foo' => 'bar', 'baz' => 'boo']);
65593 $map->get('foo'); // Some('bar')
65594 $map->get('foo')->get(); // string('bar')
65595 $map->keys(); // ['foo', 'baz']
65596 $map->values(); // ['bar', 'boo']
65597 iterator_to_array($map); // ['foo' => 'bar', 'baz' => 'boo']
65598
65599 $map->first()->get(); // ['foo', 'bar']
65600 $map->last()->get(); // ['baz', 'boo']
65601
65602 // Write Operations
65603 $map = new Map();
65604 $map->set('foo', 'bar');
65605 $map->setAll(array('bar' => 'baz', 'baz' => 'boo'));
65606 $map->remove('foo');
65607
65608 // Sort
65609 $map->sortWith('strcmp');
65610
65611 License
65612 -------
65613
65614 The code is released under the business-friendly `Apache2 license`_.
65615
65616 Documentation is subject to the `Attribution-NonCommercial-NoDerivs 3.0 Unported
65617 license`_.
65618
65619 .. _Apache2 license: http://www.apache.org/licenses/LICENSE-2.0.html
65620 .. _Attribution-NonCommercial-NoDerivs 3.0 Unported license: http://creativecommons.org/licenses/by-nc-nd/3.0/
65621
65622 <?php
65623
65624 if ( ! is_file($autoloadFile = __DIR__.'/../vendor/autoload.php')) {
65625 echo 'Could not find "vendor/autoload.php". Did you forget to run "composer install --dev"?'.PHP_EOL;
65626 exit(1);
65627 }
65628
65629 require_once $autoloadFile;<?php
65630
65631 namespace PhpCollection\Tests;
65632
65633 use PhpCollection\Sequence;
65634 use OutOfBoundsException;
65635 use stdClass;
65636
65637 class SequenceTest extends \PHPUnit_Framework_TestCase
65638 {
65639 private $seq;
65640 private $a;
65641 private $b;
65642
65643 public function testGet()
65644 {
65645 $this->assertSame(0, $this->seq->get(0));
65646 $this->assertSame($this->a, $this->seq->get(1));
65647 }
65648
65649 public function testIndexOf()
65650 {
65651 $this->assertSame(0, $this->seq->indexOf(0));
65652 $this->assertSame(1, $this->seq->indexOf($this->a));
65653 $this->assertSame(2, $this->seq->indexOf($this->b));
65654 $this->assertSame(-1, $this->seq->indexOf(1));
65655 }
65656
65657 public function testLastIndexOf()
65658 {
65659 $this->assertSame(3, $this->seq->lastIndexOf(0));
65660 $this->assertSame(1, $this->seq->lastIndexOf($this->a));
65661 $this->assertSame(2, $this->seq->lastIndexOf($this->b));
65662 $this->assertSame(-1, $this->seq->lastIndexOf(1));
65663 }
65664
65665 public function testAddSequence()
65666 {
65667 $seq = new Sequence();
65668 $seq->add(1);
65669 $seq->add(0);
65670
65671 $this->seq->addSequence($seq);
65672
65673 $this->assertSame(array(
65674 0,
65675 $this->a,
65676 $this->b,
65677 0,
65678 1,
65679 0,
65680 ), $this->seq->all());
65681 }
65682
65683 public function testIsDefinedAt()
65684 {
65685 $this->assertTrue($this->seq->isDefinedAt(0));
65686 $this->assertTrue($this->seq->isDefinedAt(1));
65687 $this->assertFalse($this->seq->isDefinedAt(9999999));
65688 }
65689
65690 public function testIndexWhere()
65691 {
65692 $this->assertSame(-1, $this->seq->indexWhere(function() { return false; }));
65693 $this->assertSame(0, $this->seq->indexWhere(function() { return true; }));
65694 }
65695
65696 public function testLastIndexWhere()
65697 {
65698 $this->assertSame(-1, $this->seq->lastIndexWhere(function() { return false; }));
65699 $this->assertSame(3, $this->seq->lastIndexWhere(function() { return true; }));
65700 }
65701
65702 public function testFirst()
65703 {
65704 $this->assertSame(0, $this->seq->first()->get());
65705 $this->assertSame(0, $this->seq->last()->get());
65706 }
65707
65708 public function testIndices()
65709 {
65710 $this->assertSame(array(0, 1, 2, 3), $this->seq->indices());
65711 }
65712
65713 public function testContains()
65714 {
65715 $this->assertTrue($this->seq->contains(0));
65716 $this->assertTrue($this->seq->contains($this->a));
65717 $this->assertFalse($this->seq->contains(9999));
65718 $this->assertFalse($this->seq->contains(new stdClass()));
65719 }
65720
65721 public function testFind()
65722 {
65723 $a = $this->a;
65724
65725 $this->assertSame($this->a, $this->seq->find(function($x) use ($a) { return $a === $x; })->get());
65726 $this->assertFalse($this->seq->find(function() { return false; })->isDefined());
65727 }
65728
65729 public function testIsEmpty()
65730 {
65731 $this->assertFalse($this->seq->isEmpty());
65732 $seq = new Sequence();
65733 $this->assertTrue($seq->isEmpty());
65734 }
65735
65736 public function testAdd()
65737 {
65738 $this->seq->add(1);
65739 $this->assertSame(array(0, $this->a, $this->b, 0, 1), $this->seq->all());
65740
65741 $this->seq->sortWith(function($a, $b) {
65742 if (is_integer($a)) {
65743 if ( ! is_integer($b)) {
65744 return -1;
65745 }
65746
65747 return $a > $b ? 1 : -1;
65748 }
65749
65750 if (is_integer($b)) {
65751 return 1;
65752 }
65753
65754 return 1;
65755 });
65756
65757 $this->assertSame(array(0, 0, 1, $this->a, $this->b), $this->seq->all());
65758 }
65759
65760 public function testUpdate()
65761 {
65762 $this->assertSame(0, $this->seq->get(0));
65763 $this->seq->update(0, 5);
65764 $this->assertSame(5, $this->seq->get(0));
65765 }
65766
65767 /**
65768 * @expectedException InvalidArgumentException
65769 * @expectedExceptionMessage There is no element at index "99999".
65770 */
65771 public function testUpdateWithNonExistentIndex()
65772 {
65773 $this->seq->update(99999, 0);
65774 }
65775
65776 public function testAddAll()
65777 {
65778 $this->seq->addAll(array(2, 1, 3));
65779 $this->assertSame(array(0, $this->a, $this->b, 0, 2, 1, 3), $this->seq->all());
65780
65781 $this->seq->sortWith(function($a, $b) {
65782 if (is_integer($a)) {
65783 if ( ! is_integer($b)) {
65784 return -1;
65785 }
65786
65787 return $a > $b ? 1 : -1;
65788 }
65789
65790 if (is_integer($b)) {
65791 return 1;
65792 }
65793
65794 return -1;
65795 });
65796
65797 $this->assertSame(array(0, 0, 1, 2, 3, $this->a, $this->b), $this->seq->all());
65798 }
65799
65800 public function testTake()
65801 {
65802 $this->assertSame(array(0), $this->seq->take(1)->all());
65803 $this->assertSame(array(0, $this->a), $this->seq->take(2)->all());
65804 $this->assertSame(array(0, $this->a, $this->b, 0), $this->seq->take(9999)->all());
65805 }
65806
65807 /**
65808 * @expectedException InvalidArgumentException
65809 * @expectedExceptionMessage $number must be greater than 0, but got -5.
65810 */
65811 public function testTakeWithNegativeNumber()
65812 {
65813 $this->seq->take(-5);
65814 }
65815
65816 public function testTakeWhile()
65817 {
65818 $this->assertSame(array(0), $this->seq->takeWhile('is_integer')->all());
65819 }
65820
65821 public function testCount()
65822 {
65823 $this->assertCount(4, $this->seq);
65824 }
65825
65826 public function testTraverse()
65827 {
65828 $this->assertSame(array(0, $this->a, $this->b, 0), iterator_to_array($this->seq));
65829 }
65830
65831 public function testDrop()
65832 {
65833 $this->assertSame(array($this->a, $this->b, 0), $this->seq->drop(1)->all());
65834 $this->assertSame(array($this->b, 0), $this->seq->drop(2)->all());
65835 $this->assertSame(array(), $this->seq->drop(9999)->all());
65836 }
65837
65838 /**
65839 * @expectedException InvalidArgumentException
65840 * @expectedExceptionMessage The number must be greater than 0, but got -5.
65841 */
65842 public function testDropWithNegativeIndex()
65843 {
65844 $this->seq->drop(-5);
65845 }
65846
65847 public function testDropRight()
65848 {
65849 $this->assertSame(array(0, $this->a, $this->b), $this->seq->dropRight(1)->all());
65850 $this->assertSame(array(0, $this->a), $this->seq->dropRight(2)->all());
65851 $this->assertSame(array(), $this->seq->dropRight(9999)->all());
65852 }
65853
65854 /**
65855 * @expectedException InvalidArgumentException
65856 * @expectedExceptionMessage The number must be greater than 0, but got -5.
65857 */
65858 public function testDropRightWithNegativeIndex()
65859 {
65860 $this->seq->dropRight(-5);
65861 }
65862
65863 public function testDropWhile()
65864 {
65865 $this->assertSame(array(0, $this->a, $this->b, 0), $this->seq->dropWhile(function() { return false; })->all());
65866 $this->assertSame(array(), $this->seq->dropWhile(function() { return true; })->all());
65867 }
65868
65869 public function testRemove()
65870 {
65871 $this->assertSame(0, $this->seq->remove(0));
65872 $this->assertSame($this->a, $this->seq->remove(0));
65873 $this->assertSame(0, $this->seq->remove(1));
65874 }
65875
65876 /**
65877 * @expectedException OutOfBoundsException
65878 * @expectedExceptionMessage The index "9999" is not in the interval [0, 4).
65879 */
65880 public function testRemoveWithInvalidIndex()
65881 {
65882 $this->seq->remove(9999);
65883 }
65884
65885 protected function setUp()
65886 {
65887 $this->seq = new Sequence();
65888 $this->seq->addAll(array(
65889 0,
65890 $this->a = new \stdClass(),
65891 $this->b = new \stdClass(),
65892 0
65893 ));
65894 }
65895 }<?php
65896
65897 namespace PhpCollection\Tests;
65898
65899 use PhpCollection\SortedSequence;
65900
65901 class SortedSequenceTest extends \PHPUnit_Framework_TestCase
65902 {
65903 private $seq;
65904 private $a;
65905 private $b;
65906
65907 public function testAdd()
65908 {
65909 $this->seq->add(1);
65910 $this->assertSame(array(0, 0, 1, $this->a, $this->b), $this->seq->all());
65911
65912 $this->seq->add(2);
65913 $this->assertSame(array(0, 0, 1, 2, $this->a, $this->b), $this->seq->all());
65914 }
65915
65916 public function testAddAll()
65917 {
65918 $this->seq->addAll(array(2, 1, 3));
65919 $this->assertSame(array(0, 0, 1, 2, 3, $this->a, $this->b), $this->seq->all());
65920
65921 $this->seq->addAll(array(2, 3, 1, 2));
65922 $this->assertSame(array(0, 0, 1, 1, 2, 2, 2, 3, 3, $this->a, $this->b), $this->seq->all());
65923 }
65924
65925 public function testTake()
65926 {
65927 $seq = $this->seq->take(2);
65928 $this->assertInstanceOf('PhpCollection\SortedSequence', $seq);
65929 $this->assertSame(array(0, 0), $seq->all());
65930 }
65931
65932 protected function setUp()
65933 {
65934 $this->seq = new SortedSequence(function($a, $b) {
65935 if (is_integer($a)) {
65936 if ( ! is_integer($b)) {
65937 return -1;
65938 }
65939
65940 return $a - $b;
65941 }
65942
65943 if (is_integer($b)) {
65944 return 1;
65945 }
65946
65947 return -1;
65948 });
65949 $this->seq->addAll(array(
65950 0,
65951 $this->a = new \stdClass,
65952 $this->b = new \stdClass,
65953 0,
65954 ));
65955 }
65956 }<?php
65957
65958 namespace PhpCollection\Tests;
65959
65960 use PhpCollection\Map;
65961
65962 class MapTest extends \PHPUnit_Framework_TestCase
65963 {
65964 private $map;
65965
65966 public function testSet()
65967 {
65968 $this->assertTrue($this->map->get('asdf')->isEmpty());
65969 $this->map->set('asdf', 'foo');
65970 $this->assertEquals('foo', $this->map->get('asdf')->get());
65971
65972 $this->assertEquals('bar', $this->map->get('foo')->get());
65973 $this->map->set('foo', 'asdf');
65974 $this->assertEquals('asdf', $this->map->get('foo')->get());
65975 }
65976
65977 public function testSetSetAll()
65978 {
65979 $this->map->setAll(array('foo' => 'asdf', 'bar' => array('foo')));
65980 $this->assertEquals(array('foo' => 'asdf', 'bar' => array('foo'), 'baz' => 'boo'), iterator_to_array($this->map));
65981 }
65982
65983 public function testAddMap()
65984 {
65985 $map = new Map();
65986 $map->set('foo', array('bar'));
65987 $this->map->addMap($map);
65988
65989 $this->assertEquals(array('foo' => array('bar'), 'bar' => 'baz', 'baz' => 'boo'), iterator_to_array($this->map));
65990 }
65991
65992 public function testRemove()
65993 {
65994 $this->assertTrue($this->map->get('foo')->isDefined());
65995 $this->assertEquals('bar', $this->map->remove('foo'));
65996 $this->assertFalse($this->map->get('foo')->isDefined());
65997 }
65998
65999 public function testClear()
66000 {
66001 $this->assertCount(3, $this->map);
66002 $this->map->clear();
66003 $this->assertCount(0, $this->map);
66004 }
66005
66006 /**
66007 * @expectedException InvalidArgumentException
66008 * @expectedExceptionMessage The map has no key named "asdfasdf".
66009 */
66010 public function testRemoveWithUnknownIndex()
66011 {
66012 $this->map->remove('asdfasdf');
66013 }
66014
66015 public function testFirst()
66016 {
66017 $this->assertEquals(array('foo', 'bar'), $this->map->first()->get());
66018 $this->map->clear();
66019 $this->assertTrue($this->map->first()->isEmpty());
66020 }
66021
66022 public function testLast()
66023 {
66024 $this->assertEquals(array('baz', 'boo'), $this->map->last()->get());
66025 $this->map->clear();
66026 $this->assertTrue($this->map->last()->isEmpty());
66027 }
66028
66029 public function testContains()
66030 {
66031 $this->assertTrue($this->map->contains('boo'));
66032 $this->assertFalse($this->map->contains('asdf'));
66033 }
66034
66035 public function testContainsKey()
66036 {
66037 $this->assertTrue($this->map->containsKey('foo'));
66038 $this->assertFalse($this->map->containsKey('boo'));
66039 }
66040
66041 public function testIsEmpty()
66042 {
66043 $this->assertFalse($this->map->isEmpty());
66044 $this->map->clear();
66045 $this->assertTrue($this->map->isEmpty());
66046 }
66047
66048 public function testDropWhile()
66049 {
66050 $newMap = $this->map->dropWhile(function($k, $v) { return 'foo' === $k || 'baz' === $v; });
66051 $this->assertEquals(array('baz' => 'boo'), iterator_to_array($newMap));
66052 $this->assertCount(3, $this->map);
66053 }
66054
66055 public function testDrop()
66056 {
66057 $newMap = $this->map->drop(2);
66058 $this->assertEquals(array('baz' => 'boo'), iterator_to_array($newMap));
66059 $this->assertCount(3, $this->map);
66060 }
66061
66062 /**
66063 * @expectedException InvalidArgumentException
66064 * @expectedExceptionMessage The number must be greater than 0, but got -4.
66065 */
66066 public function testDropWithNegativeNumber()
66067 {
66068 $this->map->drop(-4);
66069 }
66070
66071 public function testDropRight()
66072 {
66073 $newMap = $this->map->dropRight(2);
66074 $this->assertEquals(array('foo' => 'bar'), iterator_to_array($newMap));
66075 $this->assertCount(3, $this->map);
66076 }
66077
66078 /**
66079 * @expectedException InvalidArgumentException
66080 * @expectedExceptionMessage The number must be greater than 0, but got -5.
66081 */
66082 public function testDropRightWithNegativeNumber()
66083 {
66084 $this->map->dropRight(-5);
66085 }
66086
66087 public function testTake()
66088 {
66089 $newMap = $this->map->take(1);
66090 $this->assertEquals(array('foo' => 'bar'), iterator_to_array($newMap));
66091 $this->assertCount(3, $this->map);
66092 }
66093
66094 /**
66095 * @expectedException InvalidArgumentException
66096 * @expectedExceptionMessage The number must be greater than 0, but got -5.
66097 */
66098 public function testTakeWithNegativeNumber()
66099 {
66100 $this->map->take(-5);
66101 }
66102
66103 public function testTakeWhile()
66104 {
66105 $newMap = $this->map->takeWhile(function($k, $v) { return 'foo' === $k || 'baz' === $v; });
66106 $this->assertEquals(array('foo' => 'bar', 'bar' => 'baz'), iterator_to_array($newMap));
66107 $this->assertCount(3, $this->map);
66108 }
66109
66110 public function testFind()
66111 {
66112 $foundElem = $this->map->find(function($k, $v) { return 'foo' === $k && 'bar' === $v; });
66113 $this->assertEquals(array('foo', 'bar'), $foundElem->get());
66114
66115 $this->assertTrue($this->map->find(function() { return false; })->isEmpty());
66116 }
66117
66118 public function testKeys()
66119 {
66120 $this->assertEquals(array('foo', 'bar', 'baz'), $this->map->keys());
66121 }
66122
66123 public function testValues()
66124 {
66125 $this->assertEquals(array('bar', 'baz', 'boo'), $this->map->values());
66126 }
66127
66128 protected function setUp()
66129 {
66130 $this->map = new Map();
66131 $this->map->setAll(array(
66132 'foo' => 'bar',
66133 'bar' => 'baz',
66134 'baz' => 'boo',
66135 ));
66136 }
66137 }{
66138 "name": "phpcollection/phpcollection",
66139 "description": "General-Purpose Collection Library for PHP",
66140 "keywords": ["collection", "list", "sequence", "map", "set"],
66141 "license": "Apache2",
66142 "authors": [
66143 {"name": "Johannes M. Schmitt", "email": "schmittjoh@gmail.com"}
66144 ],
66145 "require": {
66146 "phpoption/phpoption": "1.*"
66147 },
66148 "autoload": {
66149 "psr-0": {
66150 "PhpCollection": "src/"
66151 }
66152 },
66153 "extra": {
66154 "branch-alias": {
66155 "dev-master": "0.2-dev"
66156 }
66157 }
66158 }<?xml version="1.0" encoding="UTF-8"?>
66159
66160 <phpunit backupGlobals="false"
66161 backupStaticAttributes="false"
66162 colors="true"
66163 convertErrorsToExceptions="true"
66164 convertNoticesToExceptions="true"
66165 convertWarningsToExceptions="true"
66166 processIsolation="false"
66167 stopOnFailure="false"
66168 syntaxCheck="false"
66169 bootstrap="tests/bootstrap.php"
66170 >
66171 <testsuites>
66172 <testsuite name="PhpCollection Test Suite">
66173 <directory>./tests/PhpCollection/</directory>
66174 </testsuite>
66175 </testsuites>
66176
66177 <groups>
66178 <exclude>
66179 <group>performance</group>
66180 </exclude>
66181 </groups>
66182 </phpunit>
66183 <?php
66184
66185 // autoload_real.php @generated by Composer
66186
66187 class ComposerAutoloaderInit09ccc38d6dd411064b9da74608e195df
66188 {
66189 private static $loader;
66190
66191 public static function loadClassLoader($class)
66192 {
66193 if ('Composer\Autoload\ClassLoader' === $class) {
66194 require __DIR__ . '/ClassLoader.php';
66195 }
66196 }
66197
66198 public static function getLoader()
66199 {
66200 if (null !== self::$loader) {
66201 return self::$loader;
66202 }
66203
66204 spl_autoload_register(array('ComposerAutoloaderInit09ccc38d6dd411064b9da74608e195df', 'loadClassLoader'), true, true);
66205 self::$loader = $loader = new \Composer\Autoload\ClassLoader();
66206 spl_autoload_unregister(array('ComposerAutoloaderInit09ccc38d6dd411064b9da74608e195df', 'loadClassLoader'));
66207
66208 $vendorDir = dirname(__DIR__);
66209 $baseDir = dirname($vendorDir);
66210
66211 $map = require __DIR__ . '/autoload_namespaces.php';
66212 foreach ($map as $namespace => $path) {
66213 $loader->set($namespace, $path);
66214 }
66215
66216 $map = require __DIR__ . '/autoload_psr4.php';
66217 foreach ($map as $namespace => $path) {
66218 $loader->setPsr4($namespace, $path);
66219 }
66220
66221 $classMap = require __DIR__ . '/autoload_classmap.php';
66222 if ($classMap) {
66223 $loader->addClassMap($classMap);
66224 }
66225
66226 $loader->register(true);
66227
66228 return $loader;
66229 }
66230 }
66231 [
66232 {
66233 "name": "doctrine/lexer",
66234 "version": "v1.0",
66235 "version_normalized": "1.0.0.0",
66236 "source": {
66237 "type": "git",
66238 "url": "https://github.com/doctrine/lexer.git",
66239 "reference": "2f708a85bb3aab5d99dab8be435abd73e0b18acb"
66240 },
66241 "dist": {
66242 "type": "zip",
66243 "url": "https://api.github.com/repos/doctrine/lexer/zipball/2f708a85bb3aab5d99dab8be435abd73e0b18acb",
66244 "reference": "2f708a85bb3aab5d99dab8be435abd73e0b18acb",
66245 "shasum": ""
66246 },
66247 "require": {
66248 "php": ">=5.3.2"
66249 },
66250 "time": "2013-01-12 18:59:04",
66251 "type": "library",
66252 "installation-source": "source",
66253 "autoload": {
66254 "psr-0": {
66255 "Doctrine\\Common\\Lexer\\": "lib/"
66256 }
66257 },
66258 "notification-url": "https://packagist.org/downloads/",
66259 "license": [
66260 "MIT"
66261 ],
66262 "authors": [
66263 {
66264 "name": "Guilherme Blanco",
66265 "email": "guilhermeblanco@gmail.com",
66266 "homepage": "http://www.instaclick.com"
66267 },
66268 {
66269 "name": "Roman Borschel",
66270 "email": "roman@code-factory.org"
66271 },
66272 {
66273 "name": "Johannes Schmitt",
66274 "email": "schmittjoh@gmail.com",
66275 "homepage": "http://jmsyst.com",
66276 "role": "Developer of wrapped JMSSerializerBundle"
66277 }
66278 ],
66279 "description": "Base library for a lexer that can be used in Top-Down, Recursive Descent Parsers.",
66280 "homepage": "http://www.doctrine-project.org",
66281 "keywords": [
66282 "lexer",
66283 "parser"
66284 ]
66285 },
66286 {
66287 "name": "symfony/event-dispatcher",
66288 "version": "v2.3.4",
66289 "version_normalized": "2.3.4.0",
66290 "target-dir": "Symfony/Component/EventDispatcher",
66291 "source": {
66292 "type": "git",
66293 "url": "https://github.com/symfony/EventDispatcher.git",
66294 "reference": "41c9826457c65fa3cf746f214985b7ca9cba42f8"
66295 },
66296 "dist": {
66297 "type": "zip",
66298 "url": "https://api.github.com/repos/symfony/EventDispatcher/zipball/41c9826457c65fa3cf746f214985b7ca9cba42f8",
66299 "reference": "41c9826457c65fa3cf746f214985b7ca9cba42f8",
66300 "shasum": ""
66301 },
66302 "require": {
66303 "php": ">=5.3.3"
66304 },
66305 "require-dev": {
66306 "symfony/dependency-injection": "~2.0"
66307 },
66308 "suggest": {
66309 "symfony/dependency-injection": "",
66310 "symfony/http-kernel": ""
66311 },
66312 "time": "2013-07-21 12:12:18",
66313 "type": "library",
66314 "extra": {
66315 "branch-alias": {
66316 "dev-master": "2.3-dev"
66317 }
66318 },
66319 "installation-source": "source",
66320 "autoload": {
66321 "psr-0": {
66322 "Symfony\\Component\\EventDispatcher\\": ""
66323 }
66324 },
66325 "notification-url": "https://packagist.org/downloads/",
66326 "license": [
66327 "MIT"
66328 ],
66329 "authors": [
66330 {
66331 "name": "Fabien Potencier",
66332 "email": "fabien@symfony.com"
66333 },
66334 {
66335 "name": "Symfony Community",
66336 "homepage": "http://symfony.com/contributors"
66337 }
66338 ],
66339 "description": "Symfony EventDispatcher Component",
66340 "homepage": "http://symfony.com"
66341 },
66342 {
66343 "name": "guzzle/guzzle",
66344 "version": "v3.7.3",
66345 "version_normalized": "3.7.3.0",
66346 "source": {
66347 "type": "git",
66348 "url": "https://github.com/guzzle/guzzle.git",
66349 "reference": "0f16aad385528b5cf790392cb4a4d16cf600e944"
66350 },
66351 "dist": {
66352 "type": "zip",
66353 "url": "https://api.github.com/repos/guzzle/guzzle/zipball/0f16aad385528b5cf790392cb4a4d16cf600e944",
66354 "reference": "0f16aad385528b5cf790392cb4a4d16cf600e944",
66355 "shasum": ""
66356 },
66357 "require": {
66358 "ext-curl": "*",
66359 "php": ">=5.3.2",
66360 "symfony/event-dispatcher": ">=2.1"
66361 },
66362 "replace": {
66363 "guzzle/batch": "self.version",
66364 "guzzle/cache": "self.version",
66365 "guzzle/common": "self.version",
66366 "guzzle/http": "self.version",
66367 "guzzle/inflection": "self.version",
66368 "guzzle/iterator": "self.version",
66369 "guzzle/log": "self.version",
66370 "guzzle/parser": "self.version",
66371 "guzzle/plugin": "self.version",
66372 "guzzle/plugin-async": "self.version",
66373 "guzzle/plugin-backoff": "self.version",
66374 "guzzle/plugin-cache": "self.version",
66375 "guzzle/plugin-cookie": "self.version",
66376 "guzzle/plugin-curlauth": "self.version",
66377 "guzzle/plugin-error-response": "self.version",
66378 "guzzle/plugin-history": "self.version",
66379 "guzzle/plugin-log": "self.version",
66380 "guzzle/plugin-md5": "self.version",
66381 "guzzle/plugin-mock": "self.version",
66382 "guzzle/plugin-oauth": "self.version",
66383 "guzzle/service": "self.version",
66384 "guzzle/stream": "self.version"
66385 },
66386 "require-dev": {
66387 "doctrine/cache": "*",
66388 "monolog/monolog": "1.*",
66389 "phpunit/phpunit": "3.7.*",
66390 "psr/log": "1.0.*",
66391 "symfony/class-loader": "*",
66392 "zendframework/zend-cache": "2.0.*",
66393 "zendframework/zend-log": "2.0.*"
66394 },
66395 "time": "2013-09-08 21:09:18",
66396 "type": "library",
66397 "extra": {
66398 "branch-alias": {
66399 "dev-master": "3.7-dev"
66400 }
66401 },
66402 "installation-source": "source",
66403 "autoload": {
66404 "psr-0": {
66405 "Guzzle\\Tests": "tests/",
66406 "Guzzle": "src/"
66407 }
66408 },
66409 "notification-url": "https://packagist.org/downloads/",
66410 "license": [
66411 "MIT"
66412 ],
66413 "authors": [
66414 {
66415 "name": "Michael Dowling",
66416 "email": "mtdowling@gmail.com",
66417 "homepage": "https://github.com/mtdowling"
66418 },
66419 {
66420 "name": "Guzzle Community",
66421 "homepage": "https://github.com/guzzle/guzzle/contributors"
66422 }
66423 ],
66424 "description": "Guzzle is a PHP HTTP client library and framework for building RESTful web service clients",
66425 "homepage": "http://guzzlephp.org/",
66426 "keywords": [
66427 "client",
66428 "curl",
66429 "framework",
66430 "http",
66431 "http client",
66432 "rest",
66433 "web service"
66434 ]
66435 },
66436 {
66437 "name": "phpoption/phpoption",
66438 "version": "1.3.0",
66439 "version_normalized": "1.3.0.0",
66440 "source": {
66441 "type": "git",
66442 "url": "https://github.com/schmittjoh/php-option.git",
66443 "reference": "1c7e8016289d17d83ced49c56d0f266fd0568941"
66444 },
66445 "dist": {
66446 "type": "zip",
66447 "url": "https://api.github.com/repos/schmittjoh/php-option/zipball/1c7e8016289d17d83ced49c56d0f266fd0568941",
66448 "reference": "1c7e8016289d17d83ced49c56d0f266fd0568941",
66449 "shasum": ""
66450 },
66451 "require": {
66452 "php": ">=5.3.0"
66453 },
66454 "time": "2013-05-19 11:09:35",
66455 "type": "library",
66456 "extra": {
66457 "branch-alias": {
66458 "dev-master": "1.3-dev"
66459 }
66460 },
66461 "installation-source": "source",
66462 "autoload": {
66463 "psr-0": {
66464 "PhpOption\\": "src/"
66465 }
66466 },
66467 "notification-url": "https://packagist.org/downloads/",
66468 "license": [
66469 "Apache2"
66470 ],
66471 "authors": [
66472 {
66473 "name": "Johannes M. Schmitt",
66474 "email": "schmittjoh@gmail.com",
66475 "homepage": "http://jmsyst.com",
66476 "role": "Developer of wrapped JMSSerializerBundle"
66477 }
66478 ],
66479 "description": "Option Type for PHP",
66480 "keywords": [
66481 "language",
66482 "option",
66483 "php",
66484 "type"
66485 ]
66486 },
66487 {
66488 "name": "phpcollection/phpcollection",
66489 "version": "0.2.0",
66490 "version_normalized": "0.2.0.0",
66491 "source": {
66492 "type": "git",
66493 "url": "https://github.com/schmittjoh/php-collection.git",
66494 "reference": "acb02a921bb364f360ce786b13455345063c4a07"
66495 },
66496 "dist": {
66497 "type": "zip",
66498 "url": "https://api.github.com/repos/schmittjoh/php-collection/zipball/acb02a921bb364f360ce786b13455345063c4a07",
66499 "reference": "acb02a921bb364f360ce786b13455345063c4a07",
66500 "shasum": ""
66501 },
66502 "require": {
66503 "phpoption/phpoption": "1.*"
66504 },
66505 "time": "2013-01-23 15:16:14",
66506 "type": "library",
66507 "extra": {
66508 "branch-alias": {
66509 "dev-master": "0.2-dev"
66510 }
66511 },
66512 "installation-source": "source",
66513 "autoload": {
66514 "psr-0": {
66515 "PhpCollection": "src/"
66516 }
66517 },
66518 "notification-url": "https://packagist.org/downloads/",
66519 "license": [
66520 "Apache2"
66521 ],
66522 "authors": [
66523 {
66524 "name": "Johannes M. Schmitt",
66525 "email": "schmittjoh@gmail.com",
66526 "homepage": "http://jmsyst.com",
66527 "role": "Developer of wrapped JMSSerializerBundle"
66528 }
66529 ],
66530 "description": "General-Purpose Collection Library for PHP",
66531 "keywords": [
66532 "collection",
66533 "list",
66534 "map",
66535 "sequence",
66536 "set"
66537 ]
66538 },
66539 {
66540 "name": "jms/parser-lib",
66541 "version": "1.0.0",
66542 "version_normalized": "1.0.0.0",
66543 "source": {
66544 "type": "git",
66545 "url": "https://github.com/schmittjoh/parser-lib.git",
66546 "reference": "c509473bc1b4866415627af0e1c6cc8ac97fa51d"
66547 },
66548 "dist": {
66549 "type": "zip",
66550 "url": "https://api.github.com/repos/schmittjoh/parser-lib/zipball/c509473bc1b4866415627af0e1c6cc8ac97fa51d",
66551 "reference": "c509473bc1b4866415627af0e1c6cc8ac97fa51d",
66552 "shasum": ""
66553 },
66554 "require": {
66555 "phpoption/phpoption": ">=0.9,<2.0-dev"
66556 },
66557 "time": "2012-11-18 18:08:43",
66558 "type": "library",
66559 "extra": {
66560 "branch-alias": {
66561 "dev-master": "1.0-dev"
66562 }
66563 },
66564 "installation-source": "source",
66565 "autoload": {
66566 "psr-0": {
66567 "JMS\\": "src/"
66568 }
66569 },
66570 "notification-url": "https://packagist.org/downloads/",
66571 "license": [
66572 "Apache2"
66573 ],
66574 "description": "A library for easily creating recursive-descent parsers."
66575 },
66576 {
66577 "name": "jms/metadata",
66578 "version": "1.4.2",
66579 "version_normalized": "1.4.2.0",
66580 "source": {
66581 "type": "git",
66582 "url": "https://github.com/schmittjoh/metadata.git",
66583 "reference": "246d7096801ce29b5aea30a1abf0277ccfb9f055"
66584 },
66585 "dist": {
66586 "type": "zip",
66587 "url": "https://api.github.com/repos/schmittjoh/metadata/zipball/246d7096801ce29b5aea30a1abf0277ccfb9f055",
66588 "reference": "246d7096801ce29b5aea30a1abf0277ccfb9f055",
66589 "shasum": ""
66590 },
66591 "require": {
66592 "php": ">=5.3.0"
66593 },
66594 "require-dev": {
66595 "doctrine/cache": "~1.0"
66596 },
66597 "time": "2013-09-13 09:05:44",
66598 "type": "library",
66599 "extra": {
66600 "branch-alias": {
66601 "dev-master": "1.4.x-dev"
66602 }
66603 },
66604 "installation-source": "source",
66605 "autoload": {
66606 "psr-0": {
66607 "Metadata\\": "src/"
66608 }
66609 },
66610 "notification-url": "https://packagist.org/downloads/",
66611 "license": [
66612 "Apache"
66613 ],
66614 "authors": [
66615 {
66616 "name": "Johannes M. Schmitt",
66617 "email": "schmittjoh@gmail.com",
66618 "homepage": "http://jmsyst.com",
66619 "role": "Developer of wrapped JMSSerializerBundle"
66620 }
66621 ],
66622 "description": "Class/method/property metadata management in PHP",
66623 "keywords": [
66624 "annotations",
66625 "metadata",
66626 "xml",
66627 "yaml"
66628 ]
66629 },
66630 {
66631 "name": "doctrine/annotations",
66632 "version": "v1.1.2",
66633 "version_normalized": "1.1.2.0",
66634 "source": {
66635 "type": "git",
66636 "url": "https://github.com/doctrine/annotations.git",
66637 "reference": "40db0c96985aab2822edbc4848b3bd2429e02670"
66638 },
66639 "dist": {
66640 "type": "zip",
66641 "url": "https://api.github.com/repos/doctrine/annotations/zipball/40db0c96985aab2822edbc4848b3bd2429e02670",
66642 "reference": "40db0c96985aab2822edbc4848b3bd2429e02670",
66643 "shasum": ""
66644 },
66645 "require": {
66646 "doctrine/lexer": "1.*",
66647 "php": ">=5.3.2"
66648 },
66649 "require-dev": {
66650 "doctrine/cache": "1.*"
66651 },
66652 "time": "2013-06-16 21:33:03",
66653 "type": "library",
66654 "extra": {
66655 "branch-alias": {
66656 "dev-master": "1.0.x-dev"
66657 }
66658 },
66659 "installation-source": "source",
66660 "autoload": {
66661 "psr-0": {
66662 "Doctrine\\Common\\Annotations\\": "lib/"
66663 }
66664 },
66665 "notification-url": "https://packagist.org/downloads/",
66666 "license": [
66667 "MIT"
66668 ],
66669 "authors": [
66670 {
66671 "name": "Jonathan Wage",
66672 "email": "jonwage@gmail.com",
66673 "homepage": "http://www.jwage.com/"
66674 },
66675 {
66676 "name": "Guilherme Blanco",
66677 "email": "guilhermeblanco@gmail.com",
66678 "homepage": "http://www.instaclick.com"
66679 },
66680 {
66681 "name": "Roman Borschel",
66682 "email": "roman@code-factory.org"
66683 },
66684 {
66685 "name": "Benjamin Eberlei",
66686 "email": "kontakt@beberlei.de"
66687 },
66688 {
66689 "name": "Johannes Schmitt",
66690 "email": "schmittjoh@gmail.com",
66691 "homepage": "http://jmsyst.com",
66692 "role": "Developer of wrapped JMSSerializerBundle"
66693 }
66694 ],
66695 "description": "Docblock Annotations Parser",
66696 "homepage": "http://www.doctrine-project.org",
66697 "keywords": [
66698 "annotations",
66699 "docblock",
66700 "parser"
66701 ]
66702 },
66703 {
66704 "name": "jms/serializer",
66705 "version": "0.13.0",
66706 "version_normalized": "0.13.0.0",
66707 "source": {
66708 "type": "git",
66709 "url": "https://github.com/schmittjoh/serializer.git",
66710 "reference": "9e0fcd00a374e9ad484687628c6c25ab1083ea5a"
66711 },
66712 "dist": {
66713 "type": "zip",
66714 "url": "https://api.github.com/repos/schmittjoh/serializer/zipball/9e0fcd00a374e9ad484687628c6c25ab1083ea5a",
66715 "reference": "9e0fcd00a374e9ad484687628c6c25ab1083ea5a",
66716 "shasum": ""
66717 },
66718 "require": {
66719 "doctrine/annotations": "1.*",
66720 "jms/metadata": "~1.1",
66721 "jms/parser-lib": "1.*",
66722 "php": ">=5.3.2",
66723 "phpcollection/phpcollection": ">=0.1,<0.3-dev"
66724 },
66725 "require-dev": {
66726 "doctrine/orm": ">=2.1,<2.4-dev",
66727 "symfony/filesystem": "2.*",
66728 "symfony/form": ">=2.1,<2.2-dev",
66729 "symfony/translation": ">=2.0,<2.2-dev",
66730 "symfony/validator": ">=2.0,<2.2-dev",
66731 "symfony/yaml": "2.*",
66732 "twig/twig": ">=1.8,<2.0-dev"
66733 },
66734 "suggest": {
66735 "symfony/yaml": "Required if you'd like to serialize data to YAML format."
66736 },
66737 "time": "2013-07-29 13:39:49",
66738 "type": "library",
66739 "extra": {
66740 "branch-alias": {
66741 "dev-master": "0.13-dev"
66742 }
66743 },
66744 "installation-source": "source",
66745 "autoload": {
66746 "psr-0": {
66747 "JMS\\Serializer": "src/"
66748 }
66749 },
66750 "notification-url": "https://packagist.org/downloads/",
66751 "license": [
66752 "Apache2"
66753 ],
66754 "authors": [
66755 {
66756 "name": "Johannes M. Schmitt",
66757 "email": "schmittjoh@gmail.com",
66758 "homepage": "https://github.com/schmittjoh",
66759 "role": "Developer of wrapped JMSSerializerBundle"
66760 }
66761 ],
66762 "description": "Library for (de-)serializing data of any complexity; supports XML, JSON, and YAML.",
66763 "homepage": "http://jmsyst.com/libs/serializer",
66764 "keywords": [
66765 "deserialization",
66766 "jaxb",
66767 "json",
66768 "serialization",
66769 "xml"
66770 ]
66771 },
66772 {
66773 "name": "symfony/console",
66774 "version": "v2.3.4",
66775 "version_normalized": "2.3.4.0",
66776 "target-dir": "Symfony/Component/Console",
66777 "source": {
66778 "type": "git",
66779 "url": "https://github.com/symfony/Console.git",
66780 "reference": "db78f8ff7fc9e28d25ff9a0bf6ddf9f0e35fbbe3"
66781 },
66782 "dist": {
66783 "type": "zip",
66784 "url": "https://api.github.com/repos/symfony/Console/zipball/db78f8ff7fc9e28d25ff9a0bf6ddf9f0e35fbbe3",
66785 "reference": "db78f8ff7fc9e28d25ff9a0bf6ddf9f0e35fbbe3",
66786 "shasum": ""
66787 },
66788 "require": {
66789 "php": ">=5.3.3"
66790 },
66791 "require-dev": {
66792 "symfony/event-dispatcher": "~2.1"
66793 },
66794 "suggest": {
66795 "symfony/event-dispatcher": ""
66796 },
66797 "time": "2013-08-17 16:34:49",
66798 "type": "library",
66799 "extra": {
66800 "branch-alias": {
66801 "dev-master": "2.3-dev"
66802 }
66803 },
66804 "installation-source": "source",
66805 "autoload": {
66806 "psr-0": {
66807 "Symfony\\Component\\Console\\": ""
66808 }
66809 },
66810 "notification-url": "https://packagist.org/downloads/",
66811 "license": [
66812 "MIT"
66813 ],
66814 "authors": [
66815 {
66816 "name": "Fabien Potencier",
66817 "email": "fabien@symfony.com"
66818 },
66819 {
66820 "name": "Symfony Community",
66821 "homepage": "http://symfony.com/contributors"
66822 }
66823 ],
66824 "description": "Symfony Console Component",
66825 "homepage": "http://symfony.com"
66826 },
66827 {
66828 "name": "symfony/process",
66829 "version": "v2.3.4",
66830 "version_normalized": "2.3.4.0",
66831 "target-dir": "Symfony/Component/Process",
66832 "source": {
66833 "type": "git",
66834 "url": "https://github.com/symfony/Process.git",
66835 "reference": "1e91553e1cedd0b8fb1da6ea4f89b02e21713d5b"
66836 },
66837 "dist": {
66838 "type": "zip",
66839 "url": "https://api.github.com/repos/symfony/Process/zipball/1e91553e1cedd0b8fb1da6ea4f89b02e21713d5b",
66840 "reference": "1e91553e1cedd0b8fb1da6ea4f89b02e21713d5b",
66841 "shasum": ""
66842 },
66843 "require": {
66844 "php": ">=5.3.3"
66845 },
66846 "time": "2013-08-22 06:42:25",
66847 "type": "library",
66848 "extra": {
66849 "branch-alias": {
66850 "dev-master": "2.3-dev"
66851 }
66852 },
66853 "installation-source": "source",
66854 "autoload": {
66855 "psr-0": {
66856 "Symfony\\Component\\Process\\": ""
66857 }
66858 },
66859 "notification-url": "https://packagist.org/downloads/",
66860 "license": [
66861 "MIT"
66862 ],
66863 "authors": [
66864 {
66865 "name": "Fabien Potencier",
66866 "email": "fabien@symfony.com"
66867 },
66868 {
66869 "name": "Symfony Community",
66870 "homepage": "http://symfony.com/contributors"
66871 }
66872 ],
66873 "description": "Symfony Process Component",
66874 "homepage": "http://symfony.com"
66875 },
66876 {
66877 "name": "symfony/filesystem",
66878 "version": "v2.3.4",
66879 "version_normalized": "2.3.4.0",
66880 "target-dir": "Symfony/Component/Filesystem",
66881 "source": {
66882 "type": "git",
66883 "url": "https://github.com/symfony/Filesystem.git",
66884 "reference": "87acbbef6d35ba649f96f09cc572c45119b360c3"
66885 },
66886 "dist": {
66887 "type": "zip",
66888 "url": "https://api.github.com/repos/symfony/Filesystem/zipball/87acbbef6d35ba649f96f09cc572c45119b360c3",
66889 "reference": "87acbbef6d35ba649f96f09cc572c45119b360c3",
66890 "shasum": ""
66891 },
66892 "require": {
66893 "php": ">=5.3.3"
66894 },
66895 "time": "2013-07-21 12:12:18",
66896 "type": "library",
66897 "extra": {
66898 "branch-alias": {
66899 "dev-master": "2.3-dev"
66900 }
66901 },
66902 "installation-source": "source",
66903 "autoload": {
66904 "psr-0": {
66905 "Symfony\\Component\\Filesystem\\": ""
66906 }
66907 },
66908 "notification-url": "https://packagist.org/downloads/",
66909 "license": [
66910 "MIT"
66911 ],
66912 "authors": [
66913 {
66914 "name": "Fabien Potencier",
66915 "email": "fabien@symfony.com"
66916 },
66917 {
66918 "name": "Symfony Community",
66919 "homepage": "http://symfony.com/contributors"
66920 }
66921 ],
66922 "description": "Symfony Filesystem Component",
66923 "homepage": "http://symfony.com"
66924 }
66925 ]
66926 <?php
66927
66928 // autoload_namespaces.php @generated by Composer
66929
66930 $vendorDir = dirname(dirname(__FILE__));
66931 $baseDir = dirname($vendorDir);
66932
66933 return array(
66934 'Symfony\\Component\\Process\\' => array($vendorDir . '/symfony/process'),
66935 'Symfony\\Component\\Filesystem\\' => array($vendorDir . '/symfony/filesystem'),
66936 'Symfony\\Component\\EventDispatcher\\' => array($vendorDir . '/symfony/event-dispatcher'),
66937 'Symfony\\Component\\Console\\' => array($vendorDir . '/symfony/console'),
66938 'Scrutinizer\\Ocular\\' => array($baseDir . '/src'),
66939 'PhpOption\\' => array($vendorDir . '/phpoption/phpoption/src'),
66940 'PhpCollection' => array($vendorDir . '/phpcollection/phpcollection/src'),
66941 'Metadata\\' => array($vendorDir . '/jms/metadata/src'),
66942 'JMS\\Serializer' => array($vendorDir . '/jms/serializer/src'),
66943 'JMS\\' => array($vendorDir . '/jms/parser-lib/src'),
66944 'Guzzle\\Tests' => array($vendorDir . '/guzzle/guzzle/tests'),
66945 'Guzzle' => array($vendorDir . '/guzzle/guzzle/src'),
66946 'Doctrine\\Common\\Lexer\\' => array($vendorDir . '/doctrine/lexer/lib'),
66947 'Doctrine\\Common\\Annotations\\' => array($vendorDir . '/doctrine/annotations/lib'),
66948 );
66949 <?php
66950
66951 // autoload_psr4.php @generated by Composer
66952
66953 $vendorDir = dirname(dirname(__FILE__));
66954 $baseDir = dirname($vendorDir);
66955
66956 return array(
66957 );
66958 <?php
66959
66960 // autoload_classmap.php @generated by Composer
66961
66962 $vendorDir = dirname(dirname(__FILE__));
66963 $baseDir = dirname($vendorDir);
66964
66965 return array(
66966 );
66967 <?php
66968
66969 /*
66970 * This file is part of Composer.
66971 *
66972 * (c) Nils Adermann <naderman@naderman.de>
66973 * Jordi Boggiano <j.boggiano@seld.be>
66974 *
66975 * For the full copyright and license information, please view the LICENSE
66976 * file that was distributed with this source code.
66977 */
66978
66979 namespace Composer\Autoload;
66980
66981 /**
66982 * ClassLoader implements a PSR-0 class loader
66983 *
66984 * See https://github.com/php-fig/fig-standards/blob/master/accepted/PSR-0.md
66985 *
66986 * $loader = new \Composer\Autoload\ClassLoader();
66987 *
66988 * // register classes with namespaces
66989 * $loader->add('Symfony\Component', __DIR__.'/component');
66990 * $loader->add('Symfony', __DIR__.'/framework');
66991 *
66992 * // activate the autoloader
66993 * $loader->register();
66994 *
66995 * // to enable searching the include path (eg. for PEAR packages)
66996 * $loader->setUseIncludePath(true);
66997 *
66998 * In this example, if you try to use a class in the Symfony\Component
66999 * namespace or one of its children (Symfony\Component\Console for instance),
67000 * the autoloader will first look for the class under the component/
67001 * directory, and it will then fallback to the framework/ directory if not
67002 * found before giving up.
67003 *
67004 * This class is loosely based on the Symfony UniversalClassLoader.
67005 *
67006 * @author Fabien Potencier <fabien@symfony.com>
67007 * @author Jordi Boggiano <j.boggiano@seld.be>
67008 */
67009 class ClassLoader
67010 {
67011 // PSR-4
67012 private $prefixLengthsPsr4 = array();
67013 private $prefixDirsPsr4 = array();
67014 private $fallbackDirsPsr4 = array();
67015
67016 // PSR-0
67017 private $prefixesPsr0 = array();
67018 private $fallbackDirsPsr0 = array();
67019
67020 private $useIncludePath = false;
67021 private $classMap = array();
67022
67023 public function getPrefixes()
67024 {
67025 return call_user_func_array('array_merge', $this->prefixesPsr0);
67026 }
67027
67028 public function getPrefixesPsr4()
67029 {
67030 return $this->prefixDirsPsr4;
67031 }
67032
67033 public function getFallbackDirs()
67034 {
67035 return $this->fallbackDirsPsr0;
67036 }
67037
67038 public function getFallbackDirsPsr4()
67039 {
67040 return $this->fallbackDirsPsr4;
67041 }
67042
67043 public function getClassMap()
67044 {
67045 return $this->classMap;
67046 }
67047
67048 /**
67049 * @param array $classMap Class to filename map
67050 */
67051 public function addClassMap(array $classMap)
67052 {
67053 if ($this->classMap) {
67054 $this->classMap = array_merge($this->classMap, $classMap);
67055 } else {
67056 $this->classMap = $classMap;
67057 }
67058 }
67059
67060 /**
67061 * Registers a set of PSR-0 directories for a given prefix, either
67062 * appending or prepending to the ones previously set for this prefix.
67063 *
67064 * @param string $prefix The prefix
67065 * @param array|string $paths The PSR-0 root directories
67066 * @param bool $prepend Whether to prepend the directories
67067 */
67068 public function add($prefix, $paths, $prepend = false)
67069 {
67070 if (!$prefix) {
67071 if ($prepend) {
67072 $this->fallbackDirsPsr0 = array_merge(
67073 (array) $paths,
67074 $this->fallbackDirsPsr0
67075 );
67076 } else {
67077 $this->fallbackDirsPsr0 = array_merge(
67078 $this->fallbackDirsPsr0,
67079 (array) $paths
67080 );
67081 }
67082
67083 return;
67084 }
67085
67086 $first = $prefix[0];
67087 if (!isset($this->prefixesPsr0[$first][$prefix])) {
67088 $this->prefixesPsr0[$first][$prefix] = (array) $paths;
67089
67090 return;
67091 }
67092 if ($prepend) {
67093 $this->prefixesPsr0[$first][$prefix] = array_merge(
67094 (array) $paths,
67095 $this->prefixesPsr0[$first][$prefix]
67096 );
67097 } else {
67098 $this->prefixesPsr0[$first][$prefix] = array_merge(
67099 $this->prefixesPsr0[$first][$prefix],
67100 (array) $paths
67101 );
67102 }
67103 }
67104
67105 /**
67106 * Registers a set of PSR-4 directories for a given namespace, either
67107 * appending or prepending to the ones previously set for this namespace.
67108 *
67109 * @param string $prefix The prefix/namespace, with trailing '\\'
67110 * @param array|string $paths The PSR-0 base directories
67111 * @param bool $prepend Whether to prepend the directories
67112 */
67113 public function addPsr4($prefix, $paths, $prepend = false)
67114 {
67115 if (!$prefix) {
67116 // Register directories for the root namespace.
67117 if ($prepend) {
67118 $this->fallbackDirsPsr4 = array_merge(
67119 (array) $paths,
67120 $this->fallbackDirsPsr4
67121 );
67122 } else {
67123 $this->fallbackDirsPsr4 = array_merge(
67124 $this->fallbackDirsPsr4,
67125 (array) $paths
67126 );
67127 }
67128 } elseif (!isset($this->prefixDirsPsr4[$prefix])) {
67129 // Register directories for a new namespace.
67130 $length = strlen($prefix);
67131 if ('\\' !== $prefix[$length - 1]) {
67132 throw new \InvalidArgumentException("A non-empty PSR-4 prefix must end with a namespace separator.");
67133 }
67134 $this->prefixLengthsPsr4[$prefix[0]][$prefix] = $length;
67135 $this->prefixDirsPsr4[$prefix] = (array) $paths;
67136 } elseif ($prepend) {
67137 // Prepend directories for an already registered namespace.
67138 $this->prefixDirsPsr4[$prefix] = array_merge(
67139 (array) $paths,
67140 $this->prefixDirsPsr4[$prefix]
67141 );
67142 } else {
67143 // Append directories for an already registered namespace.
67144 $this->prefixDirsPsr4[$prefix] = array_merge(
67145 $this->prefixDirsPsr4[$prefix],
67146 (array) $paths
67147 );
67148 }
67149 }
67150
67151 /**
67152 * Registers a set of PSR-0 directories for a given prefix,
67153 * replacing any others previously set for this prefix.
67154 *
67155 * @param string $prefix The prefix
67156 * @param array|string $paths The PSR-0 base directories
67157 */
67158 public function set($prefix, $paths)
67159 {
67160 if (!$prefix) {
67161 $this->fallbackDirsPsr0 = (array) $paths;
67162 } else {
67163 $this->prefixesPsr0[$prefix[0]][$prefix] = (array) $paths;
67164 }
67165 }
67166
67167 /**
67168 * Registers a set of PSR-4 directories for a given namespace,
67169 * replacing any others previously set for this namespace.
67170 *
67171 * @param string $prefix The prefix/namespace, with trailing '\\'
67172 * @param array|string $paths The PSR-4 base directories
67173 */
67174 public function setPsr4($prefix, $paths) {
67175 if (!$prefix) {
67176 $this->fallbackDirsPsr4 = (array) $paths;
67177 } else {
67178 $length = strlen($prefix);
67179 if ('\\' !== $prefix[$length - 1]) {
67180 throw new \InvalidArgumentException("A non-empty PSR-4 prefix must end with a namespace separator.");
67181 }
67182 $this->prefixLengthsPsr4[$prefix[0]][$prefix] = $length;
67183 $this->prefixDirsPsr4[$prefix] = (array) $paths;
67184 }
67185 }
67186
67187 /**
67188 * Turns on searching the include path for class files.
67189 *
67190 * @param bool $useIncludePath
67191 */
67192 public function setUseIncludePath($useIncludePath)
67193 {
67194 $this->useIncludePath = $useIncludePath;
67195 }
67196
67197 /**
67198 * Can be used to check if the autoloader uses the include path to check
67199 * for classes.
67200 *
67201 * @return bool
67202 */
67203 public function getUseIncludePath()
67204 {
67205 return $this->useIncludePath;
67206 }
67207
67208 /**
67209 * Registers this instance as an autoloader.
67210 *
67211 * @param bool $prepend Whether to prepend the autoloader or not
67212 */
67213 public function register($prepend = false)
67214 {
67215 spl_autoload_register(array($this, 'loadClass'), true, $prepend);
67216 }
67217
67218 /**
67219 * Unregisters this instance as an autoloader.
67220 */
67221 public function unregister()
67222 {
67223 spl_autoload_unregister(array($this, 'loadClass'));
67224 }
67225
67226 /**
67227 * Loads the given class or interface.
67228 *
67229 * @param string $class The name of the class
67230 * @return bool|null True if loaded, null otherwise
67231 */
67232 public function loadClass($class)
67233 {
67234 if ($file = $this->findFile($class)) {
67235 include $file;
67236
67237 return true;
67238 }
67239 }
67240
67241 /**
67242 * Finds the path to the file where the class is defined.
67243 *
67244 * @param string $class The name of the class
67245 *
67246 * @return string|false The path if found, false otherwise
67247 */
67248 public function findFile($class)
67249 {
67250 // work around for PHP 5.3.0 - 5.3.2 https://bugs.php.net/50731
67251 if ('\\' == $class[0]) {
67252 $class = substr($class, 1);
67253 }
67254
67255 // class map lookup
67256 if (isset($this->classMap[$class])) {
67257 return $this->classMap[$class];
67258 }
67259
67260 // PSR-4 lookup
67261 $logicalPathPsr4 = strtr($class, '\\', DIRECTORY_SEPARATOR) . '.php';
67262
67263 $first = $class[0];
67264 if (isset($this->prefixLengthsPsr4[$first])) {
67265 foreach ($this->prefixLengthsPsr4[$first] as $prefix => $length) {
67266 if (0 === strpos($class, $prefix)) {
67267 foreach ($this->prefixDirsPsr4[$prefix] as $dir) {
67268 if (file_exists($file = $dir . DIRECTORY_SEPARATOR . substr($logicalPathPsr4, $length))) {
67269 return $file;
67270 }
67271 }
67272 }
67273 }
67274 }
67275
67276 // PSR-4 fallback dirs
67277 foreach ($this->fallbackDirsPsr4 as $dir) {
67278 if (file_exists($file = $dir . DIRECTORY_SEPARATOR . $logicalPathPsr4)) {
67279 return $file;
67280 }
67281 }
67282
67283 // PSR-0 lookup
67284 if (false !== $pos = strrpos($class, '\\')) {
67285 // namespaced class name
67286 $logicalPathPsr0 = substr($logicalPathPsr4, 0, $pos + 1)
67287 . strtr(substr($logicalPathPsr4, $pos + 1), '_', DIRECTORY_SEPARATOR);
67288 } else {
67289 // PEAR-like class name
67290 $logicalPathPsr0 = strtr($class, '_', DIRECTORY_SEPARATOR) . '.php';
67291 }
67292
67293 if (isset($this->prefixesPsr0[$first])) {
67294 foreach ($this->prefixesPsr0[$first] as $prefix => $dirs) {
67295 if (0 === strpos($class, $prefix)) {
67296 foreach ($dirs as $dir) {
67297 if (file_exists($file = $dir . DIRECTORY_SEPARATOR . $logicalPathPsr0)) {
67298 return $file;
67299 }
67300 }
67301 }
67302 }
67303 }
67304
67305 // PSR-0 fallback dirs
67306 foreach ($this->fallbackDirsPsr0 as $dir) {
67307 if (file_exists($file = $dir . DIRECTORY_SEPARATOR . $logicalPathPsr0)) {
67308 return $file;
67309 }
67310 }
67311
67312 // PSR-0 include paths.
67313 if ($this->useIncludePath && $file = stream_resolve_include_path($logicalPathPsr0)) {
67314 return $file;
67315 }
67316
67317 // Remember that this class does not exist.
67318 return $this->classMap[$class] = false;
67319 }
67320 }
67321 <?php
67322
67323 /*
67324 * This file is part of the Symfony package.
67325 *
67326 * (c) Fabien Potencier <fabien@symfony.com>
67327 *
67328 * For the full copyright and license information, please view the LICENSE
67329 * file that was distributed with this source code.
67330 */
67331
67332 namespace Symfony\Component\Console\Tests\Helper;
67333
67334 use Symfony\Component\Console\Helper\HelperSet;
67335 use Symfony\Component\Console\Command\Command;
67336
67337 class HelperSetTest extends \PHPUnit_Framework_TestCase
67338 {
67339 /**
67340 * @covers \Symfony\Component\Console\Helper\HelperSet::__construct
67341 */
67342 public function testConstructor()
67343 {
67344 $mock_helper = $this->getGenericMockHelper('fake_helper');
67345 $helperset = new HelperSet(array('fake_helper_alias' => $mock_helper));
67346
67347 $this->assertEquals($mock_helper, $helperset->get('fake_helper_alias'), '__construct sets given helper to helpers');
67348 $this->assertTrue($helperset->has('fake_helper_alias'), '__construct sets helper alias for given helper');
67349 }
67350
67351 /**
67352 * @covers \Symfony\Component\Console\Helper\HelperSet::set
67353 */
67354 public function testSet()
67355 {
67356 $helperset = new HelperSet();
67357 $helperset->set($this->getGenericMockHelper('fake_helper', $helperset));
67358 $this->assertTrue($helperset->has('fake_helper'), '->set() adds helper to helpers');
67359
67360 $helperset = new HelperSet();
67361 $helperset->set($this->getGenericMockHelper('fake_helper_01', $helperset));
67362 $helperset->set($this->getGenericMockHelper('fake_helper_02', $helperset));
67363 $this->assertTrue($helperset->has('fake_helper_01'), '->set() will set multiple helpers on consecutive calls');
67364 $this->assertTrue($helperset->has('fake_helper_02'), '->set() will set multiple helpers on consecutive calls');
67365
67366 $helperset = new HelperSet();
67367 $helperset->set($this->getGenericMockHelper('fake_helper', $helperset), 'fake_helper_alias');
67368 $this->assertTrue($helperset->has('fake_helper'), '->set() adds helper alias when set');
67369 $this->assertTrue($helperset->has('fake_helper_alias'), '->set() adds helper alias when set');
67370 }
67371
67372 /**
67373 * @covers \Symfony\Component\Console\Helper\HelperSet::has
67374 */
67375 public function testHas()
67376 {
67377 $helperset = new HelperSet(array('fake_helper_alias' => $this->getGenericMockHelper('fake_helper')));
67378 $this->assertTrue($helperset->has('fake_helper'), '->has() finds set helper');
67379 $this->assertTrue($helperset->has('fake_helper_alias'), '->has() finds set helper by alias');
67380 }
67381
67382 /**
67383 * @covers \Symfony\Component\Console\Helper\HelperSet::get
67384 */
67385 public function testGet()
67386 {
67387 $helper_01 = $this->getGenericMockHelper('fake_helper_01');
67388 $helper_02 = $this->getGenericMockHelper('fake_helper_02');
67389 $helperset = new HelperSet(array('fake_helper_01_alias' => $helper_01, 'fake_helper_02_alias' => $helper_02));
67390 $this->assertEquals($helper_01, $helperset->get('fake_helper_01'), '->get() returns correct helper by name');
67391 $this->assertEquals($helper_01, $helperset->get('fake_helper_01_alias'), '->get() returns correct helper by alias');
67392 $this->assertEquals($helper_02, $helperset->get('fake_helper_02'), '->get() returns correct helper by name');
67393 $this->assertEquals($helper_02, $helperset->get('fake_helper_02_alias'), '->get() returns correct helper by alias');
67394
67395 $helperset = new HelperSet();
67396 try {
67397 $helperset->get('foo');
67398 $this->fail('->get() throws \InvalidArgumentException when helper not found');
67399 } catch (\Exception $e) {
67400 $this->assertInstanceOf('\InvalidArgumentException', $e, '->get() throws \InvalidArgumentException when helper not found');
67401 $this->assertContains('The helper "foo" is not defined.', $e->getMessage(), '->get() throws \InvalidArgumentException when helper not found');
67402 }
67403 }
67404
67405 /**
67406 * @covers \Symfony\Component\Console\Helper\HelperSet::setCommand
67407 */
67408 public function testSetCommand()
67409 {
67410 $cmd_01 = new Command('foo');
67411 $cmd_02 = new Command('bar');
67412
67413 $helperset = new HelperSet();
67414 $helperset->setCommand($cmd_01);
67415 $this->assertEquals($cmd_01, $helperset->getCommand(), '->setCommand() stores given command');
67416
67417 $helperset = new HelperSet();
67418 $helperset->setCommand($cmd_01);
67419 $helperset->setCommand($cmd_02);
67420 $this->assertEquals($cmd_02, $helperset->getCommand(), '->setCommand() overwrites stored command with consecutive calls');
67421 }
67422
67423 /**
67424 * @covers \Symfony\Component\Console\Helper\HelperSet::getCommand
67425 */
67426 public function testGetCommand()
67427 {
67428 $cmd = new Command('foo');
67429 $helperset = new HelperSet();
67430 $helperset->setCommand($cmd);
67431 $this->assertEquals($cmd, $helperset->getCommand(), '->getCommand() retrieves stored command');
67432 }
67433
67434 /**
67435 * Create a generic mock for the helper interface. Optionally check for a call to setHelperSet with a specific
67436 * helperset instance.
67437 *
67438 * @param string $name
67439 * @param HelperSet $helperset allows a mock to verify a particular helperset set is being added to the Helper
67440 */
67441 private function getGenericMockHelper($name, HelperSet $helperset = null)
67442 {
67443 $mock_helper = $this->getMock('\Symfony\Component\Console\Helper\HelperInterface');
67444 $mock_helper->expects($this->any())
67445 ->method('getName')
67446 ->will($this->returnValue($name));
67447
67448 if ($helperset) {
67449 $mock_helper->expects($this->any())
67450 ->method('setHelperSet')
67451 ->with($this->equalTo($helperset));
67452 }
67453
67454 return $mock_helper;
67455 }
67456 }
67457 <?php
67458
67459 /*
67460 * This file is part of the Symfony package.
67461 *
67462 * (c) Fabien Potencier <fabien@symfony.com>
67463 *
67464 * For the full copyright and license information, please view the LICENSE
67465 * file that was distributed with this source code.
67466 */
67467
67468 namespace Symfony\Component\Console\Tests\Helper;
67469
67470 use Symfony\Component\Console\Helper\FormatterHelper;
67471
67472 class FormatterHelperTest extends \PHPUnit_Framework_TestCase
67473 {
67474 public function testFormatSection()
67475 {
67476 $formatter = new FormatterHelper();
67477
67478 $this->assertEquals(
67479 '<info>[cli]</info> Some text to display',
67480 $formatter->formatSection('cli', 'Some text to display'),
67481 '::formatSection() formats a message in a section'
67482 );
67483 }
67484
67485 public function testFormatBlock()
67486 {
67487 $formatter = new FormatterHelper();
67488
67489 $this->assertEquals(
67490 '<error> Some text to display </error>',
67491 $formatter->formatBlock('Some text to display', 'error'),
67492 '::formatBlock() formats a message in a block'
67493 );
67494
67495 $this->assertEquals(
67496 '<error> Some text to display </error>'."\n" .
67497 '<error> foo bar </error>',
67498 $formatter->formatBlock(array('Some text to display', 'foo bar'), 'error'),
67499 '::formatBlock() formats a message in a block'
67500 );
67501
67502 $this->assertEquals(
67503 '<error> </error>'."\n" .
67504 '<error> Some text to display </error>'."\n" .
67505 '<error> </error>',
67506 $formatter->formatBlock('Some text to display', 'error', true),
67507 '::formatBlock() formats a message in a block'
67508 );
67509 }
67510
67511 public function testFormatBlockWithDiacriticLetters()
67512 {
67513 if (!extension_loaded('mbstring')) {
67514 $this->markTestSkipped('This test requires mbstring to work.');
67515 }
67516
67517 $formatter = new FormatterHelper();
67518
67519 $this->assertEquals(
67520 '<error> </error>'."\n" .
67521 '<error> Du texte à afficher </error>'."\n" .
67522 '<error> </error>',
67523 $formatter->formatBlock('Du texte à afficher', 'error', true),
67524 '::formatBlock() formats a message in a block'
67525 );
67526 }
67527
67528 public function testFormatBlockLGEscaping()
67529 {
67530 $formatter = new FormatterHelper();
67531
67532 $this->assertEquals(
67533 '<error> </error>'."\n" .
67534 '<error> \<info>some info\</info> </error>'."\n" .
67535 '<error> </error>',
67536 $formatter->formatBlock('<info>some info</info>', 'error', true),
67537 '::formatBlock() escapes \'<\' chars'
67538 );
67539 }
67540 }
67541 <?php
67542
67543 /*
67544 * This file is part of the Symfony package.
67545 *
67546 * (c) Fabien Potencier <fabien@symfony.com>
67547 *
67548 * For the full copyright and license information, please view the LICENSE
67549 * file that was distributed with this source code.
67550 */
67551
67552 namespace Symfony\Component\Console\Tests\Helper;
67553
67554 use Symfony\Component\Console\Helper\ProgressHelper;
67555 use Symfony\Component\Console\Output\StreamOutput;
67556
67557 class ProgressHelperTest extends \PHPUnit_Framework_TestCase
67558 {
67559 public function testAdvance()
67560 {
67561 $progress = new ProgressHelper();
67562 $progress->start($output = $this->getOutputStream());
67563 $progress->advance();
67564
67565 rewind($output->getStream());
67566 $this->assertEquals($this->generateOutput(' 1 [->--------------------------]'), stream_get_contents($output->getStream()));
67567 }
67568
67569 public function testAdvanceWithStep()
67570 {
67571 $progress = new ProgressHelper();
67572 $progress->start($output = $this->getOutputStream());
67573 $progress->advance(5);
67574
67575 rewind($output->getStream());
67576 $this->assertEquals($this->generateOutput(' 5 [----->----------------------]'), stream_get_contents($output->getStream()));
67577 }
67578
67579 public function testAdvanceMultipleTimes()
67580 {
67581 $progress = new ProgressHelper();
67582 $progress->start($output = $this->getOutputStream());
67583 $progress->advance(3);
67584 $progress->advance(2);
67585
67586 rewind($output->getStream());
67587 $this->assertEquals($this->generateOutput(' 3 [--->------------------------]').$this->generateOutput(' 5 [----->----------------------]'), stream_get_contents($output->getStream()));
67588 }
67589
67590 public function testCustomizations()
67591 {
67592 $progress = new ProgressHelper();
67593 $progress->setBarWidth(10);
67594 $progress->setBarCharacter('_');
67595 $progress->setEmptyBarCharacter(' ');
67596 $progress->setProgressCharacter('/');
67597 $progress->setFormat(' %current%/%max% [%bar%] %percent%%');
67598 $progress->start($output = $this->getOutputStream(), 10);
67599 $progress->advance();
67600
67601 rewind($output->getStream());
67602 $this->assertEquals($this->generateOutput(' 1/10 [_/ ] 10%'), stream_get_contents($output->getStream()));
67603 }
67604
67605 public function testPercent()
67606 {
67607 $progress = new ProgressHelper();
67608 $progress->start($output = $this->getOutputStream(), 50);
67609 $progress->display();
67610 $progress->advance();
67611 $progress->advance();
67612
67613 rewind($output->getStream());
67614 $this->assertEquals($this->generateOutput(' 0/50 [>---------------------------] 0%').$this->generateOutput(' 1/50 [>---------------------------] 2%').$this->generateOutput(' 2/50 [=>--------------------------] 4%'), stream_get_contents($output->getStream()));
67615 }
67616
67617 public function testOverwriteWithShorterLine()
67618 {
67619 $progress = new ProgressHelper();
67620 $progress->setFormat(' %current%/%max% [%bar%] %percent%%');
67621 $progress->start($output = $this->getOutputStream(), 50);
67622 $progress->display();
67623 $progress->advance();
67624
67625 // set shorter format
67626 $progress->setFormat(' %current%/%max% [%bar%]');
67627 $progress->advance();
67628
67629 rewind($output->getStream());
67630 $this->assertEquals(
67631 $this->generateOutput(' 0/50 [>---------------------------] 0%') .
67632 $this->generateOutput(' 1/50 [>---------------------------] 2%') .
67633 $this->generateOutput(' 2/50 [=>--------------------------] '),
67634 stream_get_contents($output->getStream())
67635 );
67636 }
67637
67638 public function testSetCurrentProgress()
67639 {
67640 $progress = new ProgressHelper();
67641 $progress->start($output = $this->getOutputStream(), 50);
67642 $progress->display();
67643 $progress->advance();
67644 $progress->setCurrent(15);
67645 $progress->setCurrent(25);
67646
67647 rewind($output->getStream());
67648 $this->assertEquals(
67649 $this->generateOutput(' 0/50 [>---------------------------] 0%') .
67650 $this->generateOutput(' 1/50 [>---------------------------] 2%') .
67651 $this->generateOutput(' 15/50 [========>-------------------] 30%') .
67652 $this->generateOutput(' 25/50 [==============>-------------] 50%'),
67653 stream_get_contents($output->getStream())
67654 );
67655 }
67656
67657 /**
67658 * @expectedException \LogicException
67659 * @expectedExceptionMessage You must start the progress bar
67660 */
67661 public function testSetCurrentBeforeStarting()
67662 {
67663 $progress = new ProgressHelper();
67664 $progress->setCurrent(15);
67665 }
67666
67667 /**
67668 * @expectedException \LogicException
67669 * @expectedExceptionMessage You can't regress the progress bar
67670 */
67671 public function testRegressProgress()
67672 {
67673 $progress = new ProgressHelper();
67674 $progress->start($output = $this->getOutputStream(), 50);
67675 $progress->setCurrent(15);
67676 $progress->setCurrent(10);
67677 }
67678
67679 public function testMultiByteSupport()
67680 {
67681 if (!function_exists('mb_strlen') || (false === $encoding = mb_detect_encoding('■'))) {
67682 $this->markTestSkipped('The mbstring extension is needed for multi-byte support');
67683 }
67684
67685 $progress = new ProgressHelper();
67686 $progress->start($output = $this->getOutputStream());
67687 $progress->setBarCharacter('■');
67688 $progress->advance(3);
67689
67690 rewind($output->getStream());
67691 $this->assertEquals($this->generateOutput(' 3 [■■■>------------------------]'), stream_get_contents($output->getStream()));
67692 }
67693
67694 public function testPercentNotHundredBeforeComplete()
67695 {
67696 $progress = new ProgressHelper();
67697 $progress->start($output = $this->getOutputStream(), 200);
67698 $progress->display();
67699 $progress->advance(199);
67700 $progress->advance();
67701
67702 rewind($output->getStream());
67703 $this->assertEquals($this->generateOutput(' 0/200 [>---------------------------] 0%').$this->generateOutput(' 199/200 [===========================>] 99%').$this->generateOutput(' 200/200 [============================] 100%'), stream_get_contents($output->getStream()));
67704 }
67705
67706 protected function getOutputStream()
67707 {
67708 return new StreamOutput(fopen('php://memory', 'r+', false));
67709 }
67710
67711 protected $lastMessagesLength;
67712
67713 protected function generateOutput($expected)
67714 {
67715 $expectedout = $expected;
67716
67717 if ($this->lastMessagesLength !== null) {
67718 $expectedout = str_pad($expected, $this->lastMessagesLength, "\x20", STR_PAD_RIGHT);
67719 }
67720
67721 $this->lastMessagesLength = strlen($expectedout);
67722
67723 return "\x0D".$expectedout;
67724 }
67725 }
67726 <?php
67727
67728 /*
67729 * This file is part of the Symfony package.
67730 *
67731 * (c) Fabien Potencier <fabien@symfony.com>
67732 *
67733 * For the full copyright and license information, please view the LICENSE
67734 * file that was distributed with this source code.
67735 */
67736
67737 namespace Symfony\Component\Console\Tests\Helper;
67738
67739 use Symfony\Component\Console\Helper\DialogHelper;
67740 use Symfony\Component\Console\Helper\HelperSet;
67741 use Symfony\Component\Console\Helper\FormatterHelper;
67742 use Symfony\Component\Console\Output\StreamOutput;
67743
67744 class DialogHelperTest extends \PHPUnit_Framework_TestCase
67745 {
67746 public function testSelect()
67747 {
67748 $dialog = new DialogHelper();
67749
67750 $helperSet = new HelperSet(array(new FormatterHelper()));
67751 $dialog->setHelperSet($helperSet);
67752
67753 $heroes = array('Superman', 'Batman', 'Spiderman');
67754
67755 $dialog->setInputStream($this->getInputStream("\n1\n 1 \nFabien\n1\nFabien\n1\n0,2\n 0 , 2 \n\n\n"));
67756 $this->assertEquals('2', $dialog->select($this->getOutputStream(), 'What is your favorite superhero?', $heroes, '2'));
67757 $this->assertEquals('1', $dialog->select($this->getOutputStream(), 'What is your favorite superhero?', $heroes));
67758 $this->assertEquals('1', $dialog->select($this->getOutputStream(), 'What is your favorite superhero?', $heroes));
67759 $this->assertEquals('1', $dialog->select($output = $this->getOutputStream(), 'What is your favorite superhero?', $heroes, null, false, 'Input "%s" is not a superhero!', false));
67760
67761 rewind($output->getStream());
67762 $this->assertContains('Input "Fabien" is not a superhero!', stream_get_contents($output->getStream()));
67763
67764 try {
67765 $this->assertEquals('1', $dialog->select($output = $this->getOutputStream(), 'What is your favorite superhero?', $heroes, null, 1));
67766 $this->fail();
67767 } catch (\InvalidArgumentException $e) {
67768 $this->assertEquals('Value "Fabien" is invalid', $e->getMessage());
67769 }
67770
67771 $this->assertEquals(array('1'), $dialog->select($this->getOutputStream(), 'What is your favorite superhero?', $heroes, null, false, 'Input "%s" is not a superhero!', true));
67772 $this->assertEquals(array('0', '2'), $dialog->select($this->getOutputStream(), 'What is your favorite superhero?', $heroes, null, false, 'Input "%s" is not a superhero!', true));
67773 $this->assertEquals(array('0', '2'), $dialog->select($this->getOutputStream(), 'What is your favorite superhero?', $heroes, null, false, 'Input "%s" is not a superhero!', true));
67774 $this->assertEquals(array('0', '1'), $dialog->select($this->getOutputStream(), 'What is your favorite superhero?', $heroes, '0,1', false, 'Input "%s" is not a superhero!', true));
67775 $this->assertEquals(array('0', '1'), $dialog->select($this->getOutputStream(), 'What is your favorite superhero?', $heroes, ' 0 , 1 ', false, 'Input "%s" is not a superhero!', true));
67776 }
67777
67778 public function testAsk()
67779 {
67780 $dialog = new DialogHelper();
67781
67782 $dialog->setInputStream($this->getInputStream("\n8AM\n"));
67783
67784 $this->assertEquals('2PM', $dialog->ask($this->getOutputStream(), 'What time is it?', '2PM'));
67785 $this->assertEquals('8AM', $dialog->ask($output = $this->getOutputStream(), 'What time is it?', '2PM'));
67786
67787 rewind($output->getStream());
67788 $this->assertEquals('What time is it?', stream_get_contents($output->getStream()));
67789 }
67790
67791 public function testAskWithAutocomplete()
67792 {
67793 if (!$this->hasSttyAvailable()) {
67794 $this->markTestSkipped('`stty` is required to test autocomplete functionality');
67795 }
67796
67797 // Acm<NEWLINE>
67798 // Ac<BACKSPACE><BACKSPACE>s<TAB>Test<NEWLINE>
67799 // <NEWLINE>
67800 // <UP ARROW><UP ARROW><NEWLINE>
67801 // <UP ARROW><UP ARROW><UP ARROW><UP ARROW><UP ARROW><TAB>Test<NEWLINE>
67802 // <DOWN ARROW><NEWLINE>
67803 // S<BACKSPACE><BACKSPACE><DOWN ARROW><DOWN ARROW><NEWLINE>
67804 // F00<BACKSPACE><BACKSPACE>oo<TAB><NEWLINE>
67805 $inputStream = $this->getInputStream("Acm\nAc\177\177s\tTest\n\n\033[A\033[A\n\033[A\033[A\033[A\033[A\033[A\tTest\n\033[B\nS\177\177\033[B\033[B\nF00\177\177oo\t\n");
67806
67807 $dialog = new DialogHelper();
67808 $dialog->setInputStream($inputStream);
67809
67810 $bundles = array('AcmeDemoBundle', 'AsseticBundle', 'SecurityBundle', 'FooBundle');
67811
67812 $this->assertEquals('AcmeDemoBundle', $dialog->ask($this->getOutputStream(), 'Please select a bundle', 'FrameworkBundle', $bundles));
67813 $this->assertEquals('AsseticBundleTest', $dialog->ask($this->getOutputStream(), 'Please select a bundle', 'FrameworkBundle', $bundles));
67814 $this->assertEquals('FrameworkBundle', $dialog->ask($this->getOutputStream(), 'Please select a bundle', 'FrameworkBundle', $bundles));
67815 $this->assertEquals('SecurityBundle', $dialog->ask($this->getOutputStream(), 'Please select a bundle', 'FrameworkBundle', $bundles));
67816 $this->assertEquals('FooBundleTest', $dialog->ask($this->getOutputStream(), 'Please select a bundle', 'FrameworkBundle', $bundles));
67817 $this->assertEquals('AcmeDemoBundle', $dialog->ask($this->getOutputStream(), 'Please select a bundle', 'FrameworkBundle', $bundles));
67818 $this->assertEquals('AsseticBundle', $dialog->ask($this->getOutputStream(), 'Please select a bundle', 'FrameworkBundle', $bundles));
67819 $this->assertEquals('FooBundle', $dialog->ask($this->getOutputStream(), 'Please select a bundle', 'FrameworkBundle', $bundles));
67820 }
67821
67822 public function testAskHiddenResponse()
67823 {
67824 if (defined('PHP_WINDOWS_VERSION_BUILD')) {
67825 $this->markTestSkipped('This test is not supported on Windows');
67826 }
67827
67828 $dialog = new DialogHelper();
67829
67830 $dialog->setInputStream($this->getInputStream("8AM\n"));
67831
67832 $this->assertEquals('8AM', $dialog->askHiddenResponse($this->getOutputStream(), 'What time is it?'));
67833 }
67834
67835 public function testAskConfirmation()
67836 {
67837 $dialog = new DialogHelper();
67838
67839 $dialog->setInputStream($this->getInputStream("\n\n"));
67840 $this->assertTrue($dialog->askConfirmation($this->getOutputStream(), 'Do you like French fries?'));
67841 $this->assertFalse($dialog->askConfirmation($this->getOutputStream(), 'Do you like French fries?', false));
67842
67843 $dialog->setInputStream($this->getInputStream("y\nyes\n"));
67844 $this->assertTrue($dialog->askConfirmation($this->getOutputStream(), 'Do you like French fries?', false));
67845 $this->assertTrue($dialog->askConfirmation($this->getOutputStream(), 'Do you like French fries?', false));
67846
67847 $dialog->setInputStream($this->getInputStream("n\nno\n"));
67848 $this->assertFalse($dialog->askConfirmation($this->getOutputStream(), 'Do you like French fries?', true));
67849 $this->assertFalse($dialog->askConfirmation($this->getOutputStream(), 'Do you like French fries?', true));
67850 }
67851
67852 public function testAskAndValidate()
67853 {
67854 $dialog = new DialogHelper();
67855 $helperSet = new HelperSet(array(new FormatterHelper()));
67856 $dialog->setHelperSet($helperSet);
67857
67858 $question ='What color was the white horse of Henry IV?';
67859 $error = 'This is not a color!';
67860 $validator = function ($color) use ($error) {
67861 if (!in_array($color, array('white', 'black'))) {
67862 throw new \InvalidArgumentException($error);
67863 }
67864
67865 return $color;
67866 };
67867
67868 $dialog->setInputStream($this->getInputStream("\nblack\n"));
67869 $this->assertEquals('white', $dialog->askAndValidate($this->getOutputStream(), $question, $validator, 2, 'white'));
67870 $this->assertEquals('black', $dialog->askAndValidate($this->getOutputStream(), $question, $validator, 2, 'white'));
67871
67872 $dialog->setInputStream($this->getInputStream("green\nyellow\norange\n"));
67873 try {
67874 $this->assertEquals('white', $dialog->askAndValidate($this->getOutputStream(), $question, $validator, 2, 'white'));
67875 $this->fail();
67876 } catch (\InvalidArgumentException $e) {
67877 $this->assertEquals($error, $e->getMessage());
67878 }
67879 }
67880
67881 protected function getInputStream($input)
67882 {
67883 $stream = fopen('php://memory', 'r+', false);
67884 fputs($stream, $input);
67885 rewind($stream);
67886
67887 return $stream;
67888 }
67889
67890 protected function getOutputStream()
67891 {
67892 return new StreamOutput(fopen('php://memory', 'r+', false));
67893 }
67894
67895 private function hasSttyAvailable()
67896 {
67897 exec('stty 2>&1', $output, $exitcode);
67898
67899 return $exitcode === 0;
67900 }
67901 }
67902 <?php
67903
67904 /*
67905 * This file is part of the Symfony package.
67906 *
67907 * (c) Fabien Potencier <fabien@symfony.com>
67908 *
67909 * For the full copyright and license information, please view the LICENSE
67910 * file that was distributed with this source code.
67911 */
67912
67913 namespace Symfony\Component\Console\Tests\Helper;
67914
67915 use Symfony\Component\Console\Helper\TableHelper;
67916 use Symfony\Component\Console\Output\StreamOutput;
67917
67918 class TableHelperTest extends \PHPUnit_Framework_TestCase
67919 {
67920 protected $stream;
67921
67922 protected function setUp()
67923 {
67924 $this->stream = fopen('php://memory', 'r+');
67925 }
67926
67927 protected function tearDown()
67928 {
67929 fclose($this->stream);
67930 $this->stream = null;
67931 }
67932
67933 /**
67934 * @dataProvider testRenderProvider
67935 */
67936 public function testRender($headers, $rows, $layout, $expected)
67937 {
67938 $table = new TableHelper();
67939 $table
67940 ->setHeaders($headers)
67941 ->setRows($rows)
67942 ->setLayout($layout)
67943 ;
67944 $table->render($output = $this->getOutputStream());
67945
67946 $this->assertEquals($expected, $this->getOutputContent($output));
67947 }
67948
67949 /**
67950 * @dataProvider testRenderProvider
67951 */
67952 public function testRenderAddRows($headers, $rows, $layout, $expected)
67953 {
67954 $table = new TableHelper();
67955 $table
67956 ->setHeaders($headers)
67957 ->addRows($rows)
67958 ->setLayout($layout)
67959 ;
67960 $table->render($output = $this->getOutputStream());
67961
67962 $this->assertEquals($expected, $this->getOutputContent($output));
67963 }
67964
67965 /**
67966 * @dataProvider testRenderProvider
67967 */
67968 public function testRenderAddRowsOneByOne($headers, $rows, $layout, $expected)
67969 {
67970 $table = new TableHelper();
67971 $table
67972 ->setHeaders($headers)
67973 ->setLayout($layout)
67974 ;
67975 foreach ($rows as $row) {
67976 $table->addRow($row);
67977 }
67978 $table->render($output = $this->getOutputStream());
67979
67980 $this->assertEquals($expected, $this->getOutputContent($output));
67981 }
67982
67983 public function testRenderProvider()
67984 {
67985 return array(
67986 array(
67987 array('ISBN', 'Title', 'Author'),
67988 array(
67989 array('99921-58-10-7', 'Divine Comedy', 'Dante Alighieri'),
67990 array('9971-5-0210-0', 'A Tale of Two Cities', 'Charles Dickens'),
67991 array('960-425-059-0', 'The Lord of the Rings', 'J. R. R. Tolkien'),
67992 array('80-902734-1-6', 'And Then There Were None', 'Agatha Christie'),
67993 ),
67994 TableHelper::LAYOUT_DEFAULT,
67995 <<<TABLE
67996 +---------------+--------------------------+------------------+
67997 | ISBN | Title | Author |
67998 +---------------+--------------------------+------------------+
67999 | 99921-58-10-7 | Divine Comedy | Dante Alighieri |
68000 | 9971-5-0210-0 | A Tale of Two Cities | Charles Dickens |
68001 | 960-425-059-0 | The Lord of the Rings | J. R. R. Tolkien |
68002 | 80-902734-1-6 | And Then There Were None | Agatha Christie |
68003 +---------------+--------------------------+------------------+
68004
68005 TABLE
68006 ),
68007 array(
68008 array('ISBN', 'Title', 'Author'),
68009 array(
68010 array('99921-58-10-7', 'Divine Comedy', 'Dante Alighieri'),
68011 array('9971-5-0210-0', 'A Tale of Two Cities', 'Charles Dickens'),
68012 array('960-425-059-0', 'The Lord of the Rings', 'J. R. R. Tolkien'),
68013 array('80-902734-1-6', 'And Then There Were None', 'Agatha Christie'),
68014 ),
68015 TableHelper::LAYOUT_BORDERLESS,
68016 " =============== ========================== ================== \n ISBN Title Author \n =============== ========================== ================== \n 99921-58-10-7 Divine Comedy Dante Alighieri \n 9971-5-0210-0 A Tale of Two Cities Charles Dickens \n 960-425-059-0 The Lord of the Rings J. R. R. Tolkien \n 80-902734-1-6 And Then There Were None Agatha Christie \n =============== ========================== ================== \n"
68017 ),
68018 array(
68019 array('ISBN', 'Title'),
68020 array(
68021 array('99921-58-10-7', 'Divine Comedy', 'Dante Alighieri'),
68022 array('9971-5-0210-0'),
68023 array('960-425-059-0', 'The Lord of the Rings', 'J. R. R. Tolkien'),
68024 array('80-902734-1-6', 'And Then There Were None', 'Agatha Christie'),
68025 ),
68026 TableHelper::LAYOUT_DEFAULT,
68027 <<<TABLE
68028 +---------------+--------------------------+------------------+
68029 | ISBN | Title | |
68030 +---------------+--------------------------+------------------+
68031 | 99921-58-10-7 | Divine Comedy | Dante Alighieri |
68032 | 9971-5-0210-0 | | |
68033 | 960-425-059-0 | The Lord of the Rings | J. R. R. Tolkien |
68034 | 80-902734-1-6 | And Then There Were None | Agatha Christie |
68035 +---------------+--------------------------+------------------+
68036
68037 TABLE
68038 ),
68039 array(
68040 array(),
68041 array(
68042 array('99921-58-10-7', 'Divine Comedy', 'Dante Alighieri'),
68043 array('9971-5-0210-0'),
68044 array('960-425-059-0', 'The Lord of the Rings', 'J. R. R. Tolkien'),
68045 array('80-902734-1-6', 'And Then There Were None', 'Agatha Christie'),
68046 ),
68047 TableHelper::LAYOUT_DEFAULT,
68048 <<<TABLE
68049 +---------------+--------------------------+------------------+
68050 | 99921-58-10-7 | Divine Comedy | Dante Alighieri |
68051 | 9971-5-0210-0 | | |
68052 | 960-425-059-0 | The Lord of the Rings | J. R. R. Tolkien |
68053 | 80-902734-1-6 | And Then There Were None | Agatha Christie |
68054 +---------------+--------------------------+------------------+
68055
68056 TABLE
68057 ),
68058 array(
68059 array('ISBN', 'Title'),
68060 array(),
68061 TableHelper::LAYOUT_DEFAULT,
68062 <<<TABLE
68063 +------+-------+
68064 | ISBN | Title |
68065 +------+-------+
68066
68067 TABLE
68068 ),
68069 array(
68070 array(),
68071 array(),
68072 TableHelper::LAYOUT_DEFAULT,
68073 '',
68074 ),
68075 );
68076 }
68077
68078 public function testRenderMultiByte()
68079 {
68080 if (!function_exists('mb_strlen')) {
68081 $this->markTestSkipped('The "mbstring" extension is not available');
68082 }
68083
68084 $table = new TableHelper();
68085 $table
68086 ->setHeaders(array('■■'))
68087 ->setRows(array(array(1234)))
68088 ->setLayout(TableHelper::LAYOUT_DEFAULT)
68089 ;
68090 $table->render($output = $this->getOutputStream());
68091
68092 $expected =
68093 <<<TABLE
68094 +------+
68095 | ■■ |
68096 +------+
68097 | 1234 |
68098 +------+
68099
68100 TABLE;
68101
68102 $this->assertEquals($expected, $this->getOutputContent($output));
68103 }
68104
68105 protected function getOutputStream()
68106 {
68107 return new StreamOutput($this->stream, StreamOutput::VERBOSITY_NORMAL, false);
68108 }
68109
68110 protected function getOutputContent(StreamOutput $output)
68111 {
68112 rewind($output->getStream());
68113
68114 return str_replace(PHP_EOL, "\n", stream_get_contents($output->getStream()));
68115 }
68116 }
68117 <?php
68118
68119 /*
68120 * This file is part of the Symfony package.
68121 *
68122 * (c) Fabien Potencier <fabien@symfony.com>
68123 *
68124 * For the full copyright and license information, please view the LICENSE
68125 * file that was distributed with this source code.
68126 */
68127
68128 namespace Symfony\Component\Console\Tests\Formatter;
68129
68130 use Symfony\Component\Console\Formatter\OutputFormatter;
68131 use Symfony\Component\Console\Formatter\OutputFormatterStyle;
68132
68133 class FormatterStyleTest extends \PHPUnit_Framework_TestCase
68134 {
68135 public function testEmptyTag()
68136 {
68137 $formatter = new OutputFormatter(true);
68138 $this->assertEquals("foo<>bar", $formatter->format('foo<>bar'));
68139 }
68140
68141 public function testLGCharEscaping()
68142 {
68143 $formatter = new OutputFormatter(true);
68144
68145 $this->assertEquals("foo<bar", $formatter->format('foo\\<bar'));
68146 $this->assertEquals("<info>some info</info>", $formatter->format('\\<info>some info\\</info>'));
68147 $this->assertEquals("\\<info>some info\\</info>", OutputFormatter::escape('<info>some info</info>'));
68148
68149 $this->assertEquals(
68150 "\033[33mSymfony\\Component\\Console does work very well!\033[0m",
68151 $formatter->format('<comment>Symfony\Component\Console does work very well!</comment>')
68152 );
68153 }
68154
68155 public function testBundledStyles()
68156 {
68157 $formatter = new OutputFormatter(true);
68158
68159 $this->assertTrue($formatter->hasStyle('error'));
68160 $this->assertTrue($formatter->hasStyle('info'));
68161 $this->assertTrue($formatter->hasStyle('comment'));
68162 $this->assertTrue($formatter->hasStyle('question'));
68163
68164 $this->assertEquals(
68165 "\033[37;41msome error\033[0m",
68166 $formatter->format('<error>some error</error>')
68167 );
68168 $this->assertEquals(
68169 "\033[32msome info\033[0m",
68170 $formatter->format('<info>some info</info>')
68171 );
68172 $this->assertEquals(
68173 "\033[33msome comment\033[0m",
68174 $formatter->format('<comment>some comment</comment>')
68175 );
68176 $this->assertEquals(
68177 "\033[30;46msome question\033[0m",
68178 $formatter->format('<question>some question</question>')
68179 );
68180 }
68181
68182 public function testNestedStyles()
68183 {
68184 $formatter = new OutputFormatter(true);
68185
68186 $this->assertEquals(
68187 "\033[37;41msome \033[0m\033[32msome info\033[0m\033[37;41m error\033[0m",
68188 $formatter->format('<error>some <info>some info</info> error</error>')
68189 );
68190 }
68191
68192 public function testStyleMatchingNotGreedy()
68193 {
68194 $formatter = new OutputFormatter(true);
68195
68196 $this->assertEquals(
68197 "(\033[32m>=2.0,<2.3\033[0m)",
68198 $formatter->format('(<info>>=2.0,<2.3</info>)')
68199 );
68200 }
68201
68202 public function testStyleEscaping()
68203 {
68204 $formatter = new OutputFormatter(true);
68205
68206 $this->assertEquals(
68207 "(\033[32mz>=2.0,<a2.3\033[0m)",
68208 $formatter->format('(<info>'.$formatter->escape('z>=2.0,<a2.3').'</info>)')
68209 );
68210 }
68211
68212 public function testDeepNestedStyles()
68213 {
68214 $formatter = new OutputFormatter(true);
68215
68216 $this->assertEquals(
68217 "\033[37;41merror\033[0m\033[32minfo\033[0m\033[33mcomment\033[0m\033[37;41merror\033[0m",
68218 $formatter->format('<error>error<info>info<comment>comment</info>error</error>')
68219 );
68220 }
68221
68222 public function testNewStyle()
68223 {
68224 $formatter = new OutputFormatter(true);
68225
68226 $style = new OutputFormatterStyle('blue', 'white');
68227 $formatter->setStyle('test', $style);
68228
68229 $this->assertEquals($style, $formatter->getStyle('test'));
68230 $this->assertNotEquals($style, $formatter->getStyle('info'));
68231
68232 $this->assertEquals("\033[34;47msome custom msg\033[0m", $formatter->format('<test>some custom msg</test>'));
68233 }
68234
68235 public function testRedefineStyle()
68236 {
68237 $formatter = new OutputFormatter(true);
68238
68239 $style = new OutputFormatterStyle('blue', 'white');
68240 $formatter->setStyle('info', $style);
68241
68242 $this->assertEquals("\033[34;47msome custom msg\033[0m", $formatter->format('<info>some custom msg</info>'));
68243 }
68244
68245 public function testInlineStyle()
68246 {
68247 $formatter = new OutputFormatter(true);
68248
68249 $this->assertEquals("\033[34;41msome text\033[0m", $formatter->format('<fg=blue;bg=red>some text</>'));
68250 $this->assertEquals("\033[34;41msome text\033[0m", $formatter->format('<fg=blue;bg=red>some text</fg=blue;bg=red>'));
68251 }
68252
68253 public function testNonStyleTag()
68254 {
68255 $formatter = new OutputFormatter(true);
68256 $this->assertEquals("\033[32msome \033[0m\033[32m<tag> styled\033[0m", $formatter->format('<info>some <tag> styled</info>'));
68257 }
68258
68259 public function testNotDecoratedFormatter()
68260 {
68261 $formatter = new OutputFormatter(false);
68262
68263 $this->assertTrue($formatter->hasStyle('error'));
68264 $this->assertTrue($formatter->hasStyle('info'));
68265 $this->assertTrue($formatter->hasStyle('comment'));
68266 $this->assertTrue($formatter->hasStyle('question'));
68267
68268 $this->assertEquals(
68269 "some error", $formatter->format('<error>some error</error>')
68270 );
68271 $this->assertEquals(
68272 "some info", $formatter->format('<info>some info</info>')
68273 );
68274 $this->assertEquals(
68275 "some comment", $formatter->format('<comment>some comment</comment>')
68276 );
68277 $this->assertEquals(
68278 "some question", $formatter->format('<question>some question</question>')
68279 );
68280
68281 $formatter->setDecorated(true);
68282
68283 $this->assertEquals(
68284 "\033[37;41msome error\033[0m", $formatter->format('<error>some error</error>')
68285 );
68286 $this->assertEquals(
68287 "\033[32msome info\033[0m", $formatter->format('<info>some info</info>')
68288 );
68289 $this->assertEquals(
68290 "\033[33msome comment\033[0m", $formatter->format('<comment>some comment</comment>')
68291 );
68292 $this->assertEquals(
68293 "\033[30;46msome question\033[0m", $formatter->format('<question>some question</question>')
68294 );
68295 }
68296
68297 public function testContentWithLineBreaks()
68298 {
68299 $formatter = new OutputFormatter(true);
68300
68301 $this->assertEquals(<<<EOF
68302 \033[32m
68303 some text\033[0m
68304 EOF
68305 , $formatter->format(<<<EOF
68306 <info>
68307 some text</info>
68308 EOF
68309 ));
68310
68311 $this->assertEquals(<<<EOF
68312 \033[32msome text
68313 \033[0m
68314 EOF
68315 , $formatter->format(<<<EOF
68316 <info>some text
68317 </info>
68318 EOF
68319 ));
68320
68321 $this->assertEquals(<<<EOF
68322 \033[32m
68323 some text
68324 \033[0m
68325 EOF
68326 , $formatter->format(<<<EOF
68327 <info>
68328 some text
68329 </info>
68330 EOF
68331 ));
68332
68333 $this->assertEquals(<<<EOF
68334 \033[32m
68335 some text
68336 more text
68337 \033[0m
68338 EOF
68339 , $formatter->format(<<<EOF
68340 <info>
68341 some text
68342 more text
68343 </info>
68344 EOF
68345 ));
68346 }
68347 }
68348 <?php
68349
68350 /*
68351 * This file is part of the Symfony package.
68352 *
68353 * (c) Fabien Potencier <fabien@symfony.com>
68354 *
68355 * For the full copyright and license information, please view the LICENSE
68356 * file that was distributed with this source code.
68357 */
68358
68359 namespace Symfony\Component\Console\Tests\Formatter;
68360
68361 use Symfony\Component\Console\Formatter\OutputFormatterStyle;
68362
68363 class OutputFormatterStyleTest extends \PHPUnit_Framework_TestCase
68364 {
68365 public function testConstructor()
68366 {
68367 $style = new OutputFormatterStyle('green', 'black', array('bold', 'underscore'));
68368 $this->assertEquals("\033[32;40;1;4mfoo\033[0m", $style->apply('foo'));
68369
68370 $style = new OutputFormatterStyle('red', null, array('blink'));
68371 $this->assertEquals("\033[31;5mfoo\033[0m", $style->apply('foo'));
68372
68373 $style = new OutputFormatterStyle(null, 'white');
68374 $this->assertEquals("\033[47mfoo\033[0m", $style->apply('foo'));
68375 }
68376
68377 public function testForeground()
68378 {
68379 $style = new OutputFormatterStyle();
68380
68381 $style->setForeground('black');
68382 $this->assertEquals("\033[30mfoo\033[0m", $style->apply('foo'));
68383
68384 $style->setForeground('blue');
68385 $this->assertEquals("\033[34mfoo\033[0m", $style->apply('foo'));
68386
68387 $this->setExpectedException('InvalidArgumentException');
68388 $style->setForeground('undefined-color');
68389 }
68390
68391 public function testBackground()
68392 {
68393 $style = new OutputFormatterStyle();
68394
68395 $style->setBackground('black');
68396 $this->assertEquals("\033[40mfoo\033[0m", $style->apply('foo'));
68397
68398 $style->setBackground('yellow');
68399 $this->assertEquals("\033[43mfoo\033[0m", $style->apply('foo'));
68400
68401 $this->setExpectedException('InvalidArgumentException');
68402 $style->setBackground('undefined-color');
68403 }
68404
68405 public function testOptions()
68406 {
68407 $style = new OutputFormatterStyle();
68408
68409 $style->setOptions(array('reverse', 'conceal'));
68410 $this->assertEquals("\033[7;8mfoo\033[0m", $style->apply('foo'));
68411
68412 $style->setOption('bold');
68413 $this->assertEquals("\033[7;8;1mfoo\033[0m", $style->apply('foo'));
68414
68415 $style->unsetOption('reverse');
68416 $this->assertEquals("\033[8;1mfoo\033[0m", $style->apply('foo'));
68417
68418 $style->setOption('bold');
68419 $this->assertEquals("\033[8;1mfoo\033[0m", $style->apply('foo'));
68420
68421 $style->setOptions(array('bold'));
68422 $this->assertEquals("\033[1mfoo\033[0m", $style->apply('foo'));
68423
68424 try {
68425 $style->setOption('foo');
68426 $this->fail('->setOption() throws an \InvalidArgumentException when the option does not exist in the available options');
68427 } catch (\Exception $e) {
68428 $this->assertInstanceOf('\InvalidArgumentException', $e, '->setOption() throws an \InvalidArgumentException when the option does not exist in the available options');
68429 $this->assertContains('Invalid option specified: "foo"', $e->getMessage(), '->setOption() throws an \InvalidArgumentException when the option does not exist in the available options');
68430 }
68431
68432 try {
68433 $style->unsetOption('foo');
68434 $this->fail('->unsetOption() throws an \InvalidArgumentException when the option does not exist in the available options');
68435 } catch (\Exception $e) {
68436 $this->assertInstanceOf('\InvalidArgumentException', $e, '->unsetOption() throws an \InvalidArgumentException when the option does not exist in the available options');
68437 $this->assertContains('Invalid option specified: "foo"', $e->getMessage(), '->unsetOption() throws an \InvalidArgumentException when the option does not exist in the available options');
68438 }
68439 }
68440 }
68441 <?php
68442
68443 /*
68444 * This file is part of the Symfony package.
68445 *
68446 * (c) Fabien Potencier <fabien@symfony.com>
68447 *
68448 * For the full copyright and license information, please view the LICENSE
68449 * file that was distributed with this source code.
68450 */
68451
68452 namespace Symfony\Component\Console\Tests\Formatter;
68453
68454 use Symfony\Component\Console\Formatter\OutputFormatterStyleStack;
68455 use Symfony\Component\Console\Formatter\OutputFormatterStyle;
68456
68457 class OutputFormatterStyleStackTest extends \PHPUnit_Framework_TestCase
68458 {
68459 public function testPush()
68460 {
68461 $stack = new OutputFormatterStyleStack();
68462 $stack->push($s1 = new OutputFormatterStyle('white', 'black'));
68463 $stack->push($s2 = new OutputFormatterStyle('yellow', 'blue'));
68464
68465 $this->assertEquals($s2, $stack->getCurrent());
68466
68467 $stack->push($s3 = new OutputFormatterStyle('green', 'red'));
68468
68469 $this->assertEquals($s3, $stack->getCurrent());
68470 }
68471
68472 public function testPop()
68473 {
68474 $stack = new OutputFormatterStyleStack();
68475 $stack->push($s1 = new OutputFormatterStyle('white', 'black'));
68476 $stack->push($s2 = new OutputFormatterStyle('yellow', 'blue'));
68477
68478 $this->assertEquals($s2, $stack->pop());
68479 $this->assertEquals($s1, $stack->pop());
68480 }
68481
68482 public function testPopEmpty()
68483 {
68484 $stack = new OutputFormatterStyleStack();
68485 $style = new OutputFormatterStyle();
68486
68487 $this->assertEquals($style, $stack->pop());
68488 }
68489
68490 public function testPopNotLast()
68491 {
68492 $stack = new OutputFormatterStyleStack();
68493 $stack->push($s1 = new OutputFormatterStyle('white', 'black'));
68494 $stack->push($s2 = new OutputFormatterStyle('yellow', 'blue'));
68495 $stack->push($s3 = new OutputFormatterStyle('green', 'red'));
68496
68497 $this->assertEquals($s2, $stack->pop($s2));
68498 $this->assertEquals($s1, $stack->pop());
68499 }
68500
68501 /**
68502 * @expectedException InvalidArgumentException
68503 */
68504 public function testInvalidPop()
68505 {
68506 $stack = new OutputFormatterStyleStack();
68507 $stack->push(new OutputFormatterStyle('white', 'black'));
68508 $stack->pop(new OutputFormatterStyle('yellow', 'blue'));
68509 }
68510 }
68511 <?php
68512
68513 /*
68514 * This file is part of the Symfony package.
68515 *
68516 * (c) Fabien Potencier <fabien@symfony.com>
68517 *
68518 * For the full copyright and license information, please view the LICENSE
68519 * file that was distributed with this source code.
68520 */
68521
68522 namespace Symfony\Component\Console\Tests\Output;
68523
68524 use Symfony\Component\Console\Output\Output;
68525 use Symfony\Component\Console\Output\StreamOutput;
68526
68527 class StreamOutputTest extends \PHPUnit_Framework_TestCase
68528 {
68529 protected $stream;
68530
68531 protected function setUp()
68532 {
68533 $this->stream = fopen('php://memory', 'a', false);
68534 }
68535
68536 protected function tearDown()
68537 {
68538 $this->stream = null;
68539 }
68540
68541 public function testConstructor()
68542 {
68543 $output = new StreamOutput($this->stream, Output::VERBOSITY_QUIET, true);
68544 $this->assertEquals(Output::VERBOSITY_QUIET, $output->getVerbosity(), '__construct() takes the verbosity as its first argument');
68545 $this->assertTrue($output->isDecorated(), '__construct() takes the decorated flag as its second argument');
68546 }
68547
68548 /**
68549 * @expectedException \InvalidArgumentException
68550 * @expectedExceptionMessage The StreamOutput class needs a stream as its first argument.
68551 */
68552 public function testStreamIsRequired()
68553 {
68554 new StreamOutput('foo');
68555 }
68556
68557 public function testGetStream()
68558 {
68559 $output = new StreamOutput($this->stream);
68560 $this->assertEquals($this->stream, $output->getStream(), '->getStream() returns the current stream');
68561 }
68562
68563 public function testDoWrite()
68564 {
68565 $output = new StreamOutput($this->stream);
68566 $output->writeln('foo');
68567 rewind($output->getStream());
68568 $this->assertEquals('foo'.PHP_EOL, stream_get_contents($output->getStream()), '->doWrite() writes to the stream');
68569 }
68570 }
68571 <?php
68572
68573 /*
68574 * This file is part of the Symfony package.
68575 *
68576 * (c) Fabien Potencier <fabien@symfony.com>
68577 *
68578 * For the full copyright and license information, please view the LICENSE
68579 * file that was distributed with this source code.
68580 */
68581
68582 namespace Symfony\Component\Console\Tests\Output;
68583
68584 use Symfony\Component\Console\Output\Output;
68585 use Symfony\Component\Console\Formatter\OutputFormatterStyle;
68586
68587 class OutputTest extends \PHPUnit_Framework_TestCase
68588 {
68589 public function testConstructor()
68590 {
68591 $output = new TestOutput(Output::VERBOSITY_QUIET, true);
68592 $this->assertEquals(Output::VERBOSITY_QUIET, $output->getVerbosity(), '__construct() takes the verbosity as its first argument');
68593 $this->assertTrue($output->isDecorated(), '__construct() takes the decorated flag as its second argument');
68594 }
68595
68596 public function testSetIsDecorated()
68597 {
68598 $output = new TestOutput();
68599 $output->setDecorated(true);
68600 $this->assertTrue($output->isDecorated(), 'setDecorated() sets the decorated flag');
68601 }
68602
68603 public function testSetGetVerbosity()
68604 {
68605 $output = new TestOutput();
68606 $output->setVerbosity(Output::VERBOSITY_QUIET);
68607 $this->assertEquals(Output::VERBOSITY_QUIET, $output->getVerbosity(), '->setVerbosity() sets the verbosity');
68608 }
68609
68610 public function testWriteWithVerbosityQuiet()
68611 {
68612 $output = new TestOutput(Output::VERBOSITY_QUIET);
68613 $output->writeln('foo');
68614 $this->assertEquals('', $output->output, '->writeln() outputs nothing if verbosity is set to VERBOSITY_QUIET');
68615 }
68616
68617 public function testWriteAnArrayOfMessages()
68618 {
68619 $output = new TestOutput();
68620 $output->writeln(array('foo', 'bar'));
68621 $this->assertEquals("foo\nbar\n", $output->output, '->writeln() can take an array of messages to output');
68622 }
68623
68624 /**
68625 * @dataProvider provideWriteArguments
68626 */
68627 public function testWriteRawMessage($message, $type, $expectedOutput)
68628 {
68629 $output = new TestOutput();
68630 $output->writeln($message, $type);
68631 $this->assertEquals($expectedOutput, $output->output);
68632 }
68633
68634 public function provideWriteArguments()
68635 {
68636 return array(
68637 array('<info>foo</info>', Output::OUTPUT_RAW, "<info>foo</info>\n"),
68638 array('<info>foo</info>', Output::OUTPUT_PLAIN, "foo\n"),
68639 );
68640 }
68641
68642 public function testWriteWithDecorationTurnedOff()
68643 {
68644 $output = new TestOutput();
68645 $output->setDecorated(false);
68646 $output->writeln('<info>foo</info>');
68647 $this->assertEquals("foo\n", $output->output, '->writeln() strips decoration tags if decoration is set to false');
68648 }
68649
68650 public function testWriteDecoratedMessage()
68651 {
68652 $fooStyle = new OutputFormatterStyle('yellow', 'red', array('blink'));
68653 $output = new TestOutput();
68654 $output->getFormatter()->setStyle('FOO', $fooStyle);
68655 $output->setDecorated(true);
68656 $output->writeln('<foo>foo</foo>');
68657 $this->assertEquals("\033[33;41;5mfoo\033[0m\n", $output->output, '->writeln() decorates the output');
68658 }
68659
68660 /**
68661 * @expectedException \InvalidArgumentException
68662 * @expectedExceptionMessage Unknown output type given (24)
68663 */
68664 public function testWriteWithInvalidOutputType()
68665 {
68666 $output = new TestOutput();
68667 $output->writeln('<foo>foo</foo>', 24);
68668 }
68669
68670 public function testWriteWithInvalidStyle()
68671 {
68672 $output = new TestOutput();
68673
68674 $output->clear();
68675 $output->write('<bar>foo</bar>');
68676 $this->assertEquals('<bar>foo</bar>', $output->output, '->write() do nothing when a style does not exist');
68677
68678 $output->clear();
68679 $output->writeln('<bar>foo</bar>');
68680 $this->assertEquals("<bar>foo</bar>\n", $output->output, '->writeln() do nothing when a style does not exist');
68681 }
68682 }
68683
68684 class TestOutput extends Output
68685 {
68686 public $output = '';
68687
68688 public function clear()
68689 {
68690 $this->output = '';
68691 }
68692
68693 protected function doWrite($message, $newline)
68694 {
68695 $this->output .= $message.($newline ? "\n" : '');
68696 }
68697 }
68698 <?php
68699
68700 /*
68701 * This file is part of the Symfony package.
68702 *
68703 * (c) Fabien Potencier <fabien@symfony.com>
68704 *
68705 * For the full copyright and license information, please view the LICENSE
68706 * file that was distributed with this source code.
68707 */
68708
68709 namespace Symfony\Component\Console\Tests\Output;
68710
68711 use Symfony\Component\Console\Output\ConsoleOutput;
68712 use Symfony\Component\Console\Output\Output;
68713
68714 class ConsoleOutputTest extends \PHPUnit_Framework_TestCase
68715 {
68716 public function testConstructor()
68717 {
68718 $output = new ConsoleOutput(Output::VERBOSITY_QUIET, true);
68719 $this->assertEquals(Output::VERBOSITY_QUIET, $output->getVerbosity(), '__construct() takes the verbosity as its first argument');
68720 }
68721 }
68722 <?php
68723
68724 /*
68725 * This file is part of the Symfony package.
68726 *
68727 * (c) Fabien Potencier <fabien@symfony.com>
68728 *
68729 * For the full copyright and license information, please view the LICENSE
68730 * file that was distributed with this source code.
68731 */
68732
68733 namespace Symfony\Component\Console\Tests\Output;
68734
68735 use Symfony\Component\Console\Output\NullOutput;
68736 use Symfony\Component\Console\Output\OutputInterface;
68737
68738 class NullOutputTest extends \PHPUnit_Framework_TestCase
68739 {
68740 public function testConstructor()
68741 {
68742 $output = new NullOutput();
68743
68744 ob_start();
68745 $output->write('foo');
68746 $buffer = ob_get_clean();
68747
68748 $this->assertSame('', $buffer, '->write() does nothing (at least nothing is printed)');
68749 $this->assertFalse($output->isDecorated(), '->isDecorated() returns false');
68750 }
68751
68752 public function testVerbosity()
68753 {
68754 $output = new NullOutput();
68755 $this->assertSame(OutputInterface::VERBOSITY_QUIET, $output->getVerbosity(), '->getVerbosity() returns VERBOSITY_QUIET for NullOutput by default');
68756
68757 $output->setVerbosity(OutputInterface::VERBOSITY_VERBOSE);
68758 $this->assertSame(OutputInterface::VERBOSITY_QUIET, $output->getVerbosity(), '->getVerbosity() always returns VERBOSITY_QUIET for NullOutput');
68759 }
68760 }
68761 Usage:
68762 help [--xml] [--format="..."] [--raw] [command_name]
68763
68764 Arguments:
68765 command The command to execute
68766 command_name The command name (default: "help")
68767
68768 Options:
68769 --xml To output help as XML
68770 --format To output help in other formats
68771 --raw To output raw command help
68772 --help (-h) Display this help message.
68773 --quiet (-q) Do not output any message.
68774 --verbose (-v|vv|vvv) Increase the verbosity of messages: 1 for normal output, 2 for more verbose output and 3 for debug
68775 --version (-V) Display this application version.
68776 --ansi Force ANSI output.
68777 --no-ansi Disable ANSI output.
68778 --no-interaction (-n) Do not ask any interactive question.
68779
68780 Help:
68781 The help command displays help for a given command:
68782
68783 php app/console help list
68784
68785 You can also output the help in other formats by using the --format option:
68786
68787 php app/console help --format=xml list
68788
68789 To display the list of available commands, please use the list command.
68790
68791 <?xml version="1.0" encoding="UTF-8"?>
68792 <option name="--option_name" shortcut="-o" accept_value="1" is_value_required="0" is_multiple="1">
68793 <description>option description</description>
68794 <defaults/>
68795 </option>
68796 <?php
68797
68798 use Symfony\Component\Console\Command\Command;
68799 use Symfony\Component\Console\Input\InputInterface;
68800 use Symfony\Component\Console\Output\OutputInterface;
68801
68802 class TestCommand extends Command
68803 {
68804 protected function configure()
68805 {
68806 $this
68807 ->setName('namespace:name')
68808 ->setAliases(array('name'))
68809 ->setDescription('description')
68810 ->setHelp('help')
68811 ;
68812 }
68813
68814 protected function execute(InputInterface $input, OutputInterface $output)
68815 {
68816 $output->writeln('execute called');
68817 }
68818
68819 protected function interact(InputInterface $input, OutputInterface $output)
68820 {
68821 $output->writeln('interact called');
68822 }
68823 }
68824 <info>--option_name</info> (-o) option description<comment> (multiple values allowed)</comment>
68825 descriptor:command2
68826 -------------------
68827
68828 * Description: command 2 description
68829 * Usage: `descriptor:command2 [-o|--option_name] argument_name`
68830 * Aliases: <none>
68831
68832 command 2 help
68833
68834 ### Arguments:
68835
68836 **argument_name:**
68837
68838 * Name: argument_name
68839 * Is required: yes
68840 * Is array: no
68841 * Description: <none>
68842 * Default: `NULL`
68843
68844 ### Options:
68845
68846 **option_name:**
68847
68848 * Name: `--option_name`
68849 * Shortcut: `-o`
68850 * Accept value: no
68851 * Is value required: no
68852 * Is multiple: no
68853 * Description: <none>
68854 * Default: `false`
68855 {"arguments":{"argument_name":{"name":"argument_name","is_required":true,"is_array":false,"description":"","default":null}},"options":[]}
68856 <info>argument_name</info>
68857 <?xml version="1.0" encoding="UTF-8"?>
68858 <definition>
68859 <arguments>
68860 <argument name="argument_name" is_required="1" is_array="0">
68861 <description></description>
68862 <defaults/>
68863 </argument>
68864 </arguments>
68865 <options>
68866 <option name="--option_name" shortcut="-o" accept_value="0" is_value_required="0" is_multiple="0">
68867 <description></description>
68868 </option>
68869 </options>
68870 </definition>
68871 <?php
68872
68873 use Symfony\Component\Console\Command\Command;
68874
68875 class Foo4Command extends Command
68876 {
68877 protected function configure()
68878 {
68879 $this->setName('foo3:bar:toh');
68880 }
68881 }
68882 <?php
68883
68884 /*
68885 * This file is part of the Symfony package.
68886 *
68887 * (c) Fabien Potencier <fabien@symfony.com>
68888 *
68889 * For the full copyright and license information, please view the LICENSE
68890 * file that was distributed with this source code.
68891 */
68892
68893 namespace Symfony\Component\Console\Tests\Fixtures;
68894
68895 use Symfony\Component\Console\Application;
68896
68897 class DescriptorApplication2 extends Application
68898 {
68899 public function __construct()
68900 {
68901 parent::__construct('My Symfony application', 'v1.0');
68902 $this->add(new DescriptorCommand1());
68903 $this->add(new DescriptorCommand2());
68904 }
68905 }
68906 <info>Console Tool</info>
68907
68908 <comment>Usage:</comment>
68909 [options] command [arguments]
68910
68911 <comment>Options:</comment>
68912 <info>--help</info> <info>-h</info> Display this help message.
68913 <info>--quiet</info> <info>-q</info> Do not output any message.
68914 <info>--verbose</info> <info>-v|vv|vvv</info> Increase the verbosity of messages: 1 for normal output, 2 for more verbose output and 3 for debug
68915 <info>--version</info> <info>-V</info> Display this application version.
68916 <info>--ansi</info> Force ANSI output.
68917 <info>--no-ansi</info> Disable ANSI output.
68918 <info>--no-interaction</info> <info>-n</info> Do not ask any interactive question.### Options:
68919
68920 **option_name:**
68921
68922 * Name: `--option_name`
68923 * Shortcut: `-o`
68924 * Accept value: no
68925 * Is value required: no
68926 * Is multiple: no
68927 * Description: <none>
68928 * Default: `false`
68929 <comment>Options:</comment>
68930 <info>--option_name</info> (-o)
68931 {"name":"--option_name","shortcut":"-o","accept_value":true,"is_value_required":false,"is_multiple":false,"description":"option description","default":"default_value"}
68932 <?php
68933
68934 use Symfony\Component\Console\Command\Command;
68935 use Symfony\Component\Console\Input\InputInterface;
68936 use Symfony\Component\Console\Output\OutputInterface;
68937
68938 class Foo1Command extends Command
68939 {
68940 public $input;
68941 public $output;
68942
68943 protected function configure()
68944 {
68945 $this
68946 ->setName('foo:bar1')
68947 ->setDescription('The foo:bar1 command')
68948 ->setAliases(array('afoobar1'))
68949 ;
68950 }
68951
68952 protected function execute(InputInterface $input, OutputInterface $output)
68953 {
68954 $this->input = $input;
68955 $this->output = $output;
68956 }
68957 }
68958 <comment>Usage:</comment>
68959 descriptor:command2 [-o|--option_name] argument_name
68960
68961 <comment>Arguments:</comment>
68962 <info>argument_name </info>
68963
68964 <comment>Options:</comment>
68965 <info>--option_name</info> (-o)
68966
68967 <comment>Help:</comment>
68968 command 2 help
68969 <?xml version="1.0" encoding="UTF-8"?>
68970 <definition>
68971 <arguments>
68972 <argument name="foo" is_required="0" is_array="0">
68973 <description>The foo argument</description>
68974 <defaults/>
68975 </argument>
68976 <argument name="baz" is_required="0" is_array="0">
68977 <description>The baz argument</description>
68978 <defaults>
68979 <default>true</default>
68980 </defaults>
68981 </argument>
68982 <argument name="bar" is_required="0" is_array="1">
68983 <description>The bar argument</description>
68984 <defaults>
68985 <default>bar</default>
68986 </defaults>
68987 </argument>
68988 </arguments>
68989 <options>
68990 <option name="--foo" shortcut="-f" accept_value="1" is_value_required="1" is_multiple="0">
68991 <description>The foo option</description>
68992 <defaults/>
68993 </option>
68994 <option name="--baz" shortcut="" accept_value="1" is_value_required="0" is_multiple="0">
68995 <description>The baz option</description>
68996 <defaults>
68997 <default>false</default>
68998 </defaults>
68999 </option>
69000 <option name="--bar" shortcut="-b" accept_value="1" is_value_required="0" is_multiple="0">
69001 <description>The bar option</description>
69002 <defaults>
69003 <default>bar</default>
69004 </defaults>
69005 </option>
69006 </options>
69007 </definition>
69008 {"commands":[{"name":"help","usage":"help [--xml] [--format=\"...\"] [--raw] [command_name]","description":"Displays help for a command","help":"The <info>help<\/info> command displays help for a given command:\n\n <info>php app\/console help list<\/info>\n\nYou can also output the help in other formats by using the <comment>--format<\/comment> option:\n\n <info>php app\/console help --format=xml list<\/info>\n\nTo display the list of available commands, please use the <info>list<\/info> command.","aliases":[],"definition":{"arguments":{"command_name":{"name":"command_name","is_required":false,"is_array":false,"description":"The command name","default":"help"}},"options":{"xml":{"name":"--xml","shortcut":"","accept_value":false,"is_value_required":false,"is_multiple":false,"description":"To output help as XML","default":false},"format":{"name":"--format","shortcut":"","accept_value":true,"is_value_required":true,"is_multiple":false,"description":"To output help in other formats","default":null},"raw":{"name":"--raw","shortcut":"","accept_value":false,"is_value_required":false,"is_multiple":false,"description":"To output raw command help","default":false},"help":{"name":"--help","shortcut":"-h","accept_value":false,"is_value_required":false,"is_multiple":false,"description":"Display this help message.","default":false},"quiet":{"name":"--quiet","shortcut":"-q","accept_value":false,"is_value_required":false,"is_multiple":false,"description":"Do not output any message.","default":false},"verbose":{"name":"--verbose","shortcut":"-v|-vv|-vvv","accept_value":false,"is_value_required":false,"is_multiple":false,"description":"Increase the verbosity of messages: 1 for normal output, 2 for more verbose output and 3 for debug","default":false},"version":{"name":"--version","shortcut":"-V","accept_value":false,"is_value_required":false,"is_multiple":false,"description":"Display this application version.","default":false},"ansi":{"name":"--ansi","shortcut":"","accept_value":false,"is_value_required":false,"is_multiple":false,"description":"Force ANSI output.","default":false},"no-ansi":{"name":"--no-ansi","shortcut":"","accept_value":false,"is_value_required":false,"is_multiple":false,"description":"Disable ANSI output.","default":false},"no-interaction":{"name":"--no-interaction","shortcut":"-n","accept_value":false,"is_value_required":false,"is_multiple":false,"description":"Do not ask any interactive question.","default":false}}}},{"name":"list","usage":"list [--xml] [--raw] [--format=\"...\"] [namespace]","description":"Lists commands","help":"The <info>list<\/info> command lists all commands:\n\n <info>php app\/console list<\/info>\n\nYou can also display the commands for a specific namespace:\n\n <info>php app\/console list test<\/info>\n\nYou can also output the information in other formats by using the <comment>--format<\/comment> option:\n\n <info>php app\/console list --format=xml<\/info>\n\nIt's also possible to get raw list of commands (useful for embedding command runner):\n\n <info>php app\/console list --raw<\/info>","aliases":[],"definition":{"arguments":{"namespace":{"name":"namespace","is_required":false,"is_array":false,"description":"The namespace name","default":null}},"options":{"xml":{"name":"--xml","shortcut":"","accept_value":false,"is_value_required":false,"is_multiple":false,"description":"To output list as XML","default":false},"raw":{"name":"--raw","shortcut":"","accept_value":false,"is_value_required":false,"is_multiple":false,"description":"To output raw command list","default":false},"format":{"name":"--format","shortcut":"","accept_value":true,"is_value_required":true,"is_multiple":false,"description":"To output list in other formats","default":null}}}},{"name":"descriptor:command1","usage":"descriptor:command1","description":"command 1 description","help":"command 1 help","aliases":["alias1","alias2"],"definition":{"arguments":[],"options":{"help":{"name":"--help","shortcut":"-h","accept_value":false,"is_value_required":false,"is_multiple":false,"description":"Display this help message.","default":false},"quiet":{"name":"--quiet","shortcut":"-q","accept_value":false,"is_value_required":false,"is_multiple":false,"description":"Do not output any message.","default":false},"verbose":{"name":"--verbose","shortcut":"-v|-vv|-vvv","accept_value":false,"is_value_required":false,"is_multiple":false,"description":"Increase the verbosity of messages: 1 for normal output, 2 for more verbose output and 3 for debug","default":false},"version":{"name":"--version","shortcut":"-V","accept_value":false,"is_value_required":false,"is_multiple":false,"description":"Display this application version.","default":false},"ansi":{"name":"--ansi","shortcut":"","accept_value":false,"is_value_required":false,"is_multiple":false,"description":"Force ANSI output.","default":false},"no-ansi":{"name":"--no-ansi","shortcut":"","accept_value":false,"is_value_required":false,"is_multiple":false,"description":"Disable ANSI output.","default":false},"no-interaction":{"name":"--no-interaction","shortcut":"-n","accept_value":false,"is_value_required":false,"is_multiple":false,"description":"Do not ask any interactive question.","default":false}}}},{"name":"descriptor:command2","usage":"descriptor:command2 [-o|--option_name] argument_name","description":"command 2 description","help":"command 2 help","aliases":[],"definition":{"arguments":{"argument_name":{"name":"argument_name","is_required":true,"is_array":false,"description":"","default":null}},"options":{"option_name":{"name":"--option_name","shortcut":"-o","accept_value":false,"is_value_required":false,"is_multiple":false,"description":"","default":false},"help":{"name":"--help","shortcut":"-h","accept_value":false,"is_value_required":false,"is_multiple":false,"description":"Display this help message.","default":false},"quiet":{"name":"--quiet","shortcut":"-q","accept_value":false,"is_value_required":false,"is_multiple":false,"description":"Do not output any message.","default":false},"verbose":{"name":"--verbose","shortcut":"-v|-vv|-vvv","accept_value":false,"is_value_required":false,"is_multiple":false,"description":"Increase the verbosity of messages: 1 for normal output, 2 for more verbose output and 3 for debug","default":false},"version":{"name":"--version","shortcut":"-V","accept_value":false,"is_value_required":false,"is_multiple":false,"description":"Display this application version.","default":false},"ansi":{"name":"--ansi","shortcut":"","accept_value":false,"is_value_required":false,"is_multiple":false,"description":"Force ANSI output.","default":false},"no-ansi":{"name":"--no-ansi","shortcut":"","accept_value":false,"is_value_required":false,"is_multiple":false,"description":"Disable ANSI output.","default":false},"no-interaction":{"name":"--no-interaction","shortcut":"-n","accept_value":false,"is_value_required":false,"is_multiple":false,"description":"Do not ask any interactive question.","default":false}}}}],"namespaces":[{"id":"_global","commands":["alias1","alias2","help","list"]},{"id":"descriptor","commands":["descriptor:command1","descriptor:command2"]}]}
69009 <info>Console Tool</info>
69010
69011 <comment>Usage:</comment>
69012 [options] command [arguments]
69013
69014 <comment>Options:</comment>
69015 <info>--help</info> <info>-h</info> Display this help message.
69016 <info>--quiet</info> <info>-q</info> Do not output any message.
69017 <info>--verbose</info> <info>-v|vv|vvv</info> Increase the verbosity of messages: 1 for normal output, 2 for more verbose output and 3 for debug
69018 <info>--version</info> <info>-V</info> Display this application version.
69019 <info>--ansi</info> Force ANSI output.
69020 <info>--no-ansi</info> Disable ANSI output.
69021 <info>--no-interaction</info> <info>-n</info> Do not ask any interactive question.
69022
69023 <comment>Available commands:</comment>
69024 <info>afoobar </info> The foo:bar command
69025 <info>help </info> Displays help for a command
69026 <info>list </info> Lists commands
69027 <comment>foo</comment>
69028 <info>foo:bar </info> The foo:bar command**argument_name:**
69029
69030 * Name: argument_name
69031 * Is required: yes
69032 * Is array: no
69033 * Description: <none>
69034 * Default: `NULL`
69035 **argument_name:**
69036
69037 * Name: argument_name
69038 * Is required: no
69039 * Is array: yes
69040 * Description: argument description
69041 * Default: `array ()`
69042 {"name":"descriptor:command2","usage":"descriptor:command2 [-o|--option_name] argument_name","description":"command 2 description","help":"command 2 help","aliases":[],"definition":{"arguments":{"argument_name":{"name":"argument_name","is_required":true,"is_array":false,"description":"","default":null}},"options":{"option_name":{"name":"--option_name","shortcut":"-o","accept_value":false,"is_value_required":false,"is_multiple":false,"description":"","default":false}}}}
69043 <?xml version="1.0" encoding="UTF-8"?>
69044 <definition>
69045 <arguments/>
69046 <options>
69047 <option name="--option_name" shortcut="-o" accept_value="0" is_value_required="0" is_multiple="0">
69048 <description></description>
69049 </option>
69050 </options>
69051 </definition>
69052 <?php
69053
69054 use Symfony\Component\Console\Command\Command;
69055 use Symfony\Component\Console\Input\InputInterface;
69056 use Symfony\Component\Console\Output\OutputInterface;
69057
69058 class Foo2Command extends Command
69059 {
69060 protected function configure()
69061 {
69062 $this
69063 ->setName('foo1:bar')
69064 ->setDescription('The foo1:bar command')
69065 ->setAliases(array('afoobar2'))
69066 ;
69067 }
69068
69069 protected function execute(InputInterface $input, OutputInterface $output)
69070 {
69071 }
69072 }
69073 **option_name:**
69074
69075 * Name: `--option_name`
69076 * Shortcut: `-o`
69077 * Accept value: yes
69078 * Is value required: yes
69079 * Is multiple: no
69080 * Description: option description
69081 * Default: `NULL`
69082 <info>--option_name</info> (-o)
69083 {"name":"argument_name","is_required":false,"is_array":false,"description":"argument description","default":"default_value"}
69084 <info>--option_name</info> (-o) option description<comment> (default: "default_value")</comment>
69085 <?xml version="1.0" encoding="UTF-8"?>
69086 <definition>
69087 <arguments/>
69088 <options/>
69089 </definition>
69090
69091
69092
69093 [InvalidArgumentException]
69094 The "--foo" option does not exist.
69095
69096
69097
69098 list [--xml] [--raw] [--format="..."] [namespace]
69099
69100
69101 <?xml version="1.0" encoding="UTF-8"?>
69102 <command id="descriptor:command2" name="descriptor:command2">
69103 <usage>descriptor:command2 [-o|--option_name] argument_name</usage>
69104 <description>command 2 description</description>
69105 <help>command 2 help</help>
69106 <aliases/>
69107 <arguments>
69108 <argument name="argument_name" is_required="1" is_array="0">
69109 <description></description>
69110 <defaults/>
69111 </argument>
69112 </arguments>
69113 <options>
69114 <option name="--option_name" shortcut="-o" accept_value="0" is_value_required="0" is_multiple="0">
69115 <description></description>
69116 </option>
69117 </options>
69118 </command>
69119 {"name":"descriptor:command1","usage":"descriptor:command1","description":"command 1 description","help":"command 1 help","aliases":["alias1","alias2"],"definition":{"arguments":[],"options":[]}}
69120 {"commands":[{"name":"help","usage":"help [--xml] [--format=\"...\"] [--raw] [command_name]","description":"Displays help for a command","help":"The <info>help<\/info> command displays help for a given command:\n\n <info>php app\/console help list<\/info>\n\nYou can also output the help in other formats by using the <comment>--format<\/comment> option:\n\n <info>php app\/console help --format=xml list<\/info>\n\nTo display the list of available commands, please use the <info>list<\/info> command.","aliases":[],"definition":{"arguments":{"command_name":{"name":"command_name","is_required":false,"is_array":false,"description":"The command name","default":"help"}},"options":{"xml":{"name":"--xml","shortcut":"","accept_value":false,"is_value_required":false,"is_multiple":false,"description":"To output help as XML","default":false},"format":{"name":"--format","shortcut":"","accept_value":true,"is_value_required":true,"is_multiple":false,"description":"To output help in other formats","default":null},"raw":{"name":"--raw","shortcut":"","accept_value":false,"is_value_required":false,"is_multiple":false,"description":"To output raw command help","default":false},"help":{"name":"--help","shortcut":"-h","accept_value":false,"is_value_required":false,"is_multiple":false,"description":"Display this help message.","default":false},"quiet":{"name":"--quiet","shortcut":"-q","accept_value":false,"is_value_required":false,"is_multiple":false,"description":"Do not output any message.","default":false},"verbose":{"name":"--verbose","shortcut":"-v|-vv|-vvv","accept_value":false,"is_value_required":false,"is_multiple":false,"description":"Increase the verbosity of messages: 1 for normal output, 2 for more verbose output and 3 for debug","default":false},"version":{"name":"--version","shortcut":"-V","accept_value":false,"is_value_required":false,"is_multiple":false,"description":"Display this application version.","default":false},"ansi":{"name":"--ansi","shortcut":"","accept_value":false,"is_value_required":false,"is_multiple":false,"description":"Force ANSI output.","default":false},"no-ansi":{"name":"--no-ansi","shortcut":"","accept_value":false,"is_value_required":false,"is_multiple":false,"description":"Disable ANSI output.","default":false},"no-interaction":{"name":"--no-interaction","shortcut":"-n","accept_value":false,"is_value_required":false,"is_multiple":false,"description":"Do not ask any interactive question.","default":false}}}},{"name":"list","usage":"list [--xml] [--raw] [--format=\"...\"] [namespace]","description":"Lists commands","help":"The <info>list<\/info> command lists all commands:\n\n <info>php app\/console list<\/info>\n\nYou can also display the commands for a specific namespace:\n\n <info>php app\/console list test<\/info>\n\nYou can also output the information in other formats by using the <comment>--format<\/comment> option:\n\n <info>php app\/console list --format=xml<\/info>\n\nIt's also possible to get raw list of commands (useful for embedding command runner):\n\n <info>php app\/console list --raw<\/info>","aliases":[],"definition":{"arguments":{"namespace":{"name":"namespace","is_required":false,"is_array":false,"description":"The namespace name","default":null}},"options":{"xml":{"name":"--xml","shortcut":"","accept_value":false,"is_value_required":false,"is_multiple":false,"description":"To output list as XML","default":false},"raw":{"name":"--raw","shortcut":"","accept_value":false,"is_value_required":false,"is_multiple":false,"description":"To output raw command list","default":false},"format":{"name":"--format","shortcut":"","accept_value":true,"is_value_required":true,"is_multiple":false,"description":"To output list in other formats","default":null}}}}],"namespaces":[{"id":"_global","commands":["help","list"]}]}
69121 {"name":"argument_name","is_required":true,"is_array":false,"description":"","default":null}
69122 {"name":"--option_name","shortcut":"-o","accept_value":true,"is_value_required":false,"is_multiple":true,"description":"option description","default":[]}
69123 <?xml version="1.0" encoding="UTF-8"?>
69124 <symfony>
69125 <commands>
69126 <command id="help" name="help">
69127 <usage>help [--xml] [--format="..."] [--raw] [command_name]</usage>
69128 <description>Displays help for a command</description>
69129 <help>The &lt;info&gt;help&lt;/info&gt; command displays help for a given command:
69130
69131 &lt;info&gt;php app/console help list&lt;/info&gt;
69132
69133 You can also output the help in other formats by using the &lt;comment&gt;--format&lt;/comment&gt; option:
69134
69135 &lt;info&gt;php app/console help --format=xml list&lt;/info&gt;
69136
69137 To display the list of available commands, please use the &lt;info&gt;list&lt;/info&gt; command.</help>
69138 <aliases/>
69139 <arguments>
69140 <argument name="command_name" is_required="0" is_array="0">
69141 <description>The command name</description>
69142 <defaults>
69143 <default>help</default>
69144 </defaults>
69145 </argument>
69146 </arguments>
69147 <options>
69148 <option name="--xml" shortcut="" accept_value="0" is_value_required="0" is_multiple="0">
69149 <description>To output help as XML</description>
69150 </option>
69151 <option name="--format" shortcut="" accept_value="1" is_value_required="1" is_multiple="0">
69152 <description>To output help in other formats</description>
69153 <defaults/>
69154 </option>
69155 <option name="--raw" shortcut="" accept_value="0" is_value_required="0" is_multiple="0">
69156 <description>To output raw command help</description>
69157 </option>
69158 <option name="--help" shortcut="-h" accept_value="0" is_value_required="0" is_multiple="0">
69159 <description>Display this help message.</description>
69160 </option>
69161 <option name="--quiet" shortcut="-q" accept_value="0" is_value_required="0" is_multiple="0">
69162 <description>Do not output any message.</description>
69163 </option>
69164 <option name="--verbose" shortcut="-v" shortcuts="-v|-vv|-vvv" accept_value="0" is_value_required="0" is_multiple="0">
69165 <description>Increase the verbosity of messages: 1 for normal output, 2 for more verbose output and 3 for debug</description>
69166 </option>
69167 <option name="--version" shortcut="-V" accept_value="0" is_value_required="0" is_multiple="0">
69168 <description>Display this application version.</description>
69169 </option>
69170 <option name="--ansi" shortcut="" accept_value="0" is_value_required="0" is_multiple="0">
69171 <description>Force ANSI output.</description>
69172 </option>
69173 <option name="--no-ansi" shortcut="" accept_value="0" is_value_required="0" is_multiple="0">
69174 <description>Disable ANSI output.</description>
69175 </option>
69176 <option name="--no-interaction" shortcut="-n" accept_value="0" is_value_required="0" is_multiple="0">
69177 <description>Do not ask any interactive question.</description>
69178 </option>
69179 </options>
69180 </command>
69181 <command id="list" name="list">
69182 <usage>list [--xml] [--raw] [--format="..."] [namespace]</usage>
69183 <description>Lists commands</description>
69184 <help>The &lt;info&gt;list&lt;/info&gt; command lists all commands:
69185
69186 &lt;info&gt;php app/console list&lt;/info&gt;
69187
69188 You can also display the commands for a specific namespace:
69189
69190 &lt;info&gt;php app/console list test&lt;/info&gt;
69191
69192 You can also output the information in other formats by using the &lt;comment&gt;--format&lt;/comment&gt; option:
69193
69194 &lt;info&gt;php app/console list --format=xml&lt;/info&gt;
69195
69196 It's also possible to get raw list of commands (useful for embedding command runner):
69197
69198 &lt;info&gt;php app/console list --raw&lt;/info&gt;</help>
69199 <aliases/>
69200 <arguments>
69201 <argument name="namespace" is_required="0" is_array="0">
69202 <description>The namespace name</description>
69203 <defaults/>
69204 </argument>
69205 </arguments>
69206 <options>
69207 <option name="--xml" shortcut="" accept_value="0" is_value_required="0" is_multiple="0">
69208 <description>To output list as XML</description>
69209 </option>
69210 <option name="--raw" shortcut="" accept_value="0" is_value_required="0" is_multiple="0">
69211 <description>To output raw command list</description>
69212 </option>
69213 <option name="--format" shortcut="" accept_value="1" is_value_required="1" is_multiple="0">
69214 <description>To output list in other formats</description>
69215 <defaults/>
69216 </option>
69217 </options>
69218 </command>
69219 <command id="descriptor:command1" name="descriptor:command1">
69220 <usage>descriptor:command1</usage>
69221 <description>command 1 description</description>
69222 <help>command 1 help</help>
69223 <aliases>
69224 <alias>alias1</alias>
69225 <alias>alias2</alias>
69226 </aliases>
69227 <arguments/>
69228 <options>
69229 <option name="--help" shortcut="-h" accept_value="0" is_value_required="0" is_multiple="0">
69230 <description>Display this help message.</description>
69231 </option>
69232 <option name="--quiet" shortcut="-q" accept_value="0" is_value_required="0" is_multiple="0">
69233 <description>Do not output any message.</description>
69234 </option>
69235 <option name="--verbose" shortcut="-v" shortcuts="-v|-vv|-vvv" accept_value="0" is_value_required="0" is_multiple="0">
69236 <description>Increase the verbosity of messages: 1 for normal output, 2 for more verbose output and 3 for debug</description>
69237 </option>
69238 <option name="--version" shortcut="-V" accept_value="0" is_value_required="0" is_multiple="0">
69239 <description>Display this application version.</description>
69240 </option>
69241 <option name="--ansi" shortcut="" accept_value="0" is_value_required="0" is_multiple="0">
69242 <description>Force ANSI output.</description>
69243 </option>
69244 <option name="--no-ansi" shortcut="" accept_value="0" is_value_required="0" is_multiple="0">
69245 <description>Disable ANSI output.</description>
69246 </option>
69247 <option name="--no-interaction" shortcut="-n" accept_value="0" is_value_required="0" is_multiple="0">
69248 <description>Do not ask any interactive question.</description>
69249 </option>
69250 </options>
69251 </command>
69252 <command id="descriptor:command2" name="descriptor:command2">
69253 <usage>descriptor:command2 [-o|--option_name] argument_name</usage>
69254 <description>command 2 description</description>
69255 <help>command 2 help</help>
69256 <aliases/>
69257 <arguments>
69258 <argument name="argument_name" is_required="1" is_array="0">
69259 <description></description>
69260 <defaults/>
69261 </argument>
69262 </arguments>
69263 <options>
69264 <option name="--option_name" shortcut="-o" accept_value="0" is_value_required="0" is_multiple="0">
69265 <description></description>
69266 </option>
69267 <option name="--help" shortcut="-h" accept_value="0" is_value_required="0" is_multiple="0">
69268 <description>Display this help message.</description>
69269 </option>
69270 <option name="--quiet" shortcut="-q" accept_value="0" is_value_required="0" is_multiple="0">
69271 <description>Do not output any message.</description>
69272 </option>
69273 <option name="--verbose" shortcut="-v" shortcuts="-v|-vv|-vvv" accept_value="0" is_value_required="0" is_multiple="0">
69274 <description>Increase the verbosity of messages: 1 for normal output, 2 for more verbose output and 3 for debug</description>
69275 </option>
69276 <option name="--version" shortcut="-V" accept_value="0" is_value_required="0" is_multiple="0">
69277 <description>Display this application version.</description>
69278 </option>
69279 <option name="--ansi" shortcut="" accept_value="0" is_value_required="0" is_multiple="0">
69280 <description>Force ANSI output.</description>
69281 </option>
69282 <option name="--no-ansi" shortcut="" accept_value="0" is_value_required="0" is_multiple="0">
69283 <description>Disable ANSI output.</description>
69284 </option>
69285 <option name="--no-interaction" shortcut="-n" accept_value="0" is_value_required="0" is_multiple="0">
69286 <description>Do not ask any interactive question.</description>
69287 </option>
69288 </options>
69289 </command>
69290 </commands>
69291 <namespaces>
69292 <namespace id="_global">
69293 <command>alias1</command>
69294 <command>alias2</command>
69295 <command>help</command>
69296 <command>list</command>
69297 </namespace>
69298 <namespace id="descriptor">
69299 <command>descriptor:command1</command>
69300 <command>descriptor:command2</command>
69301 </namespace>
69302 </namespaces>
69303 </symfony>
69304 **argument_name:**
69305
69306 * Name: argument_name
69307 * Is required: no
69308 * Is array: no
69309 * Description: argument description
69310 * Default: `'default_value'`
69311
69312
69313
69314 [Exception]
69315 Second exception
69316
69317
69318
69319
69320
69321
69322 [Exception]
69323 First exception
69324
69325
69326
69327 foo3:bar
69328
69329
69330 <?xml version="1.0" encoding="UTF-8"?>
69331 <argument name="argument_name" is_required="0" is_array="0">
69332 <description>argument description</description>
69333 <defaults>
69334 <default>default_value</default>
69335 </defaults>
69336 </argument>
69337 {"name":"argument_name","is_required":false,"is_array":true,"description":"argument description","default":[]}
69338 <info>argument_name</info> argument description
69339 <?xml version="1.0" encoding="UTF-8"?>
69340 <symfony>
69341 <commands namespace="foo">
69342 <command id="foo:bar" name="foo:bar">
69343 <usage>foo:bar</usage>
69344 <description>The foo:bar command</description>
69345 <help/>
69346 <aliases>
69347 <alias>afoobar</alias>
69348 </aliases>
69349 <arguments/>
69350 <options>
69351 <option name="--help" shortcut="-h" accept_value="0" is_value_required="0" is_multiple="0">
69352 <description>Display this help message.</description>
69353 </option>
69354 <option name="--quiet" shortcut="-q" accept_value="0" is_value_required="0" is_multiple="0">
69355 <description>Do not output any message.</description>
69356 </option>
69357 <option name="--verbose" shortcut="-v" shortcuts="-v|-vv|-vvv" accept_value="0" is_value_required="0" is_multiple="0">
69358 <description>Increase the verbosity of messages: 1 for normal output, 2 for more verbose output and 3 for debug</description>
69359 </option>
69360 <option name="--version" shortcut="-V" accept_value="0" is_value_required="0" is_multiple="0">
69361 <description>Display this application version.</description>
69362 </option>
69363 <option name="--ansi" shortcut="" accept_value="0" is_value_required="0" is_multiple="0">
69364 <description>Force ANSI output.</description>
69365 </option>
69366 <option name="--no-ansi" shortcut="" accept_value="0" is_value_required="0" is_multiple="0">
69367 <description>Disable ANSI output.</description>
69368 </option>
69369 <option name="--no-interaction" shortcut="-n" accept_value="0" is_value_required="0" is_multiple="0">
69370 <description>Do not ask any interactive question.</description>
69371 </option>
69372 </options>
69373 </command>
69374 </commands>
69375 </symfony>
69376 <info>argument_name</info> argument description<comment> (default: "default_value")</comment>
69377 ### Arguments:
69378
69379 **argument_name:**
69380
69381 * Name: argument_name
69382 * Is required: yes
69383 * Is array: no
69384 * Description: <none>
69385 * Default: `NULL`
69386
69387 ### Options:
69388
69389 **option_name:**
69390
69391 * Name: `--option_name`
69392 * Shortcut: `-o`
69393 * Accept value: no
69394 * Is value required: no
69395 * Is multiple: no
69396 * Description: <none>
69397 * Default: `false`
69398 <?xml version="1.0" encoding="UTF-8"?>
69399 <argument name="argument_name" is_required="1" is_array="0">
69400 <description></description>
69401 <defaults/>
69402 </argument>
69403 <?xml version="1.0" encoding="UTF-8"?>
69404 <symfony>
69405 <commands>
69406 <command id="help" name="help">
69407 <usage>help [--xml] [--format="..."] [--raw] [command_name]</usage>
69408 <description>Displays help for a command</description>
69409 <help>The &lt;info&gt;help&lt;/info&gt; command displays help for a given command:
69410
69411 &lt;info&gt;php app/console help list&lt;/info&gt;
69412
69413 You can also output the help in other formats by using the &lt;comment&gt;--format&lt;/comment&gt; option:
69414
69415 &lt;info&gt;php app/console help --format=xml list&lt;/info&gt;
69416
69417 To display the list of available commands, please use the &lt;info&gt;list&lt;/info&gt; command.</help>
69418 <aliases />
69419 <arguments>
69420 <argument name="command_name" is_required="0" is_array="0">
69421 <description>The command name</description>
69422 <defaults>
69423 <default>help</default>
69424 </defaults>
69425 </argument>
69426 </arguments>
69427 <options>
69428 <option name="--xml" shortcut="" accept_value="0" is_value_required="0" is_multiple="0">
69429 <description>To output help as XML</description>
69430 </option>
69431 <option name="--format" shortcut="" accept_value="1" is_value_required="1" is_multiple="0">
69432 <description>To output help in other formats</description>
69433 <defaults/>
69434 </option>
69435 <option name="--raw" shortcut="" accept_value="0" is_value_required="0" is_multiple="0">
69436 <description>To output raw command help</description>
69437 </option>
69438 <option name="--help" shortcut="-h" accept_value="0" is_value_required="0" is_multiple="0">
69439 <description>Display this help message.</description>
69440 </option>
69441 <option name="--quiet" shortcut="-q" accept_value="0" is_value_required="0" is_multiple="0">
69442 <description>Do not output any message.</description>
69443 </option>
69444 <option name="--verbose" shortcut="-v" shortcuts="-v|-vv|-vvv" accept_value="0" is_value_required="0" is_multiple="0">
69445 <description>Increase the verbosity of messages: 1 for normal output, 2 for more verbose output and 3 for debug</description>
69446 </option>
69447 <option name="--version" shortcut="-V" accept_value="0" is_value_required="0" is_multiple="0">
69448 <description>Display this application version.</description>
69449 </option>
69450 <option name="--ansi" shortcut="" accept_value="0" is_value_required="0" is_multiple="0">
69451 <description>Force ANSI output.</description>
69452 </option>
69453 <option name="--no-ansi" shortcut="" accept_value="0" is_value_required="0" is_multiple="0">
69454 <description>Disable ANSI output.</description>
69455 </option>
69456 <option name="--no-interaction" shortcut="-n" accept_value="0" is_value_required="0" is_multiple="0">
69457 <description>Do not ask any interactive question.</description>
69458 </option>
69459 </options>
69460 </command>
69461 <command id="list" name="list">
69462 <usage>list [--xml] [--raw] [--format="..."] [namespace]</usage>
69463 <description>Lists commands</description>
69464 <help>The &lt;info&gt;list&lt;/info&gt; command lists all commands:
69465
69466 &lt;info&gt;php app/console list&lt;/info&gt;
69467
69468 You can also display the commands for a specific namespace:
69469
69470 &lt;info&gt;php app/console list test&lt;/info&gt;
69471
69472 You can also output the information in other formats by using the &lt;comment&gt;--format&lt;/comment&gt; option:
69473
69474 &lt;info&gt;php app/console list --format=xml&lt;/info&gt;
69475
69476 It's also possible to get raw list of commands (useful for embedding command runner):
69477
69478 &lt;info&gt;php app/console list --raw&lt;/info&gt;</help>
69479 <aliases/>
69480 <arguments>
69481 <argument name="namespace" is_required="0" is_array="0">
69482 <description>The namespace name</description>
69483 <defaults/>
69484 </argument>
69485 </arguments>
69486 <options>
69487 <option name="--xml" shortcut="" accept_value="0" is_value_required="0" is_multiple="0">
69488 <description>To output list as XML</description>
69489 </option>
69490 <option name="--raw" shortcut="" accept_value="0" is_value_required="0" is_multiple="0">
69491 <description>To output raw command list</description>
69492 </option>
69493 <option name="--format" shortcut="" accept_value="1" is_value_required="1" is_multiple="0">
69494 <description>To output list in other formats</description>
69495 <defaults/>
69496 </option>
69497 </options>
69498 </command>
69499 <command id="foo:bar" name="foo:bar">
69500 <usage>foo:bar</usage>
69501 <description>The foo:bar command</description>
69502 <help/>
69503 <aliases>
69504 <alias>afoobar</alias>
69505 </aliases>
69506 <arguments/>
69507 <options>
69508 <option name="--help" shortcut="-h" accept_value="0" is_value_required="0" is_multiple="0">
69509 <description>Display this help message.</description>
69510 </option>
69511 <option name="--quiet" shortcut="-q" accept_value="0" is_value_required="0" is_multiple="0">
69512 <description>Do not output any message.</description>
69513 </option>
69514 <option name="--verbose" shortcut="-v" shortcuts="-v|-vv|-vvv" accept_value="0" is_value_required="0" is_multiple="0">
69515 <description>Increase the verbosity of messages: 1 for normal output, 2 for more verbose output and 3 for debug</description>
69516 </option>
69517 <option name="--version" shortcut="-V" accept_value="0" is_value_required="0" is_multiple="0">
69518 <description>Display this application version.</description>
69519 </option>
69520 <option name="--ansi" shortcut="" accept_value="0" is_value_required="0" is_multiple="0">
69521 <description>Force ANSI output.</description>
69522 </option>
69523 <option name="--no-ansi" shortcut="" accept_value="0" is_value_required="0" is_multiple="0">
69524 <description>Disable ANSI output.</description>
69525 </option>
69526 <option name="--no-interaction" shortcut="-n" accept_value="0" is_value_required="0" is_multiple="0">
69527 <description>Do not ask any interactive question.</description>
69528 </option>
69529 </options>
69530 </command>
69531 </commands>
69532 <namespaces>
69533 <namespace id="_global">
69534 <command>afoobar</command>
69535 <command>help</command>
69536 <command>list</command>
69537 </namespace>
69538 <namespace id="foo">
69539 <command>foo:bar</command>
69540 </namespace>
69541 </namespaces>
69542 </symfony>
69543 UNKNOWN
69544 =======
69545
69546 * help
69547 * list
69548
69549 help
69550 ----
69551
69552 * Description: Displays help for a command
69553 * Usage: `help [--xml] [--format="..."] [--raw] [command_name]`
69554 * Aliases: <none>
69555
69556 The <info>help</info> command displays help for a given command:
69557
69558 <info>php app/console help list</info>
69559
69560 You can also output the help in other formats by using the <comment>--format</comment> option:
69561
69562 <info>php app/console help --format=xml list</info>
69563
69564 To display the list of available commands, please use the <info>list</info> command.
69565
69566 ### Arguments:
69567
69568 **command_name:**
69569
69570 * Name: command_name
69571 * Is required: no
69572 * Is array: no
69573 * Description: The command name
69574 * Default: `'help'`
69575
69576 ### Options:
69577
69578 **xml:**
69579
69580 * Name: `--xml`
69581 * Shortcut: <none>
69582 * Accept value: no
69583 * Is value required: no
69584 * Is multiple: no
69585 * Description: To output help as XML
69586 * Default: `false`
69587
69588 **format:**
69589
69590 * Name: `--format`
69591 * Shortcut: <none>
69592 * Accept value: yes
69593 * Is value required: yes
69594 * Is multiple: no
69595 * Description: To output help in other formats
69596 * Default: `NULL`
69597
69598 **raw:**
69599
69600 * Name: `--raw`
69601 * Shortcut: <none>
69602 * Accept value: no
69603 * Is value required: no
69604 * Is multiple: no
69605 * Description: To output raw command help
69606 * Default: `false`
69607
69608 **help:**
69609
69610 * Name: `--help`
69611 * Shortcut: `-h`
69612 * Accept value: no
69613 * Is value required: no
69614 * Is multiple: no
69615 * Description: Display this help message.
69616 * Default: `false`
69617
69618 **quiet:**
69619
69620 * Name: `--quiet`
69621 * Shortcut: `-q`
69622 * Accept value: no
69623 * Is value required: no
69624 * Is multiple: no
69625 * Description: Do not output any message.
69626 * Default: `false`
69627
69628 **verbose:**
69629
69630 * Name: `--verbose`
69631 * Shortcut: `-v|-vv|-vvv`
69632 * Accept value: no
69633 * Is value required: no
69634 * Is multiple: no
69635 * Description: Increase the verbosity of messages: 1 for normal output, 2 for more verbose output and 3 for debug
69636 * Default: `false`
69637
69638 **version:**
69639
69640 * Name: `--version`
69641 * Shortcut: `-V`
69642 * Accept value: no
69643 * Is value required: no
69644 * Is multiple: no
69645 * Description: Display this application version.
69646 * Default: `false`
69647
69648 **ansi:**
69649
69650 * Name: `--ansi`
69651 * Shortcut: <none>
69652 * Accept value: no
69653 * Is value required: no
69654 * Is multiple: no
69655 * Description: Force ANSI output.
69656 * Default: `false`
69657
69658 **no-ansi:**
69659
69660 * Name: `--no-ansi`
69661 * Shortcut: <none>
69662 * Accept value: no
69663 * Is value required: no
69664 * Is multiple: no
69665 * Description: Disable ANSI output.
69666 * Default: `false`
69667
69668 **no-interaction:**
69669
69670 * Name: `--no-interaction`
69671 * Shortcut: `-n`
69672 * Accept value: no
69673 * Is value required: no
69674 * Is multiple: no
69675 * Description: Do not ask any interactive question.
69676 * Default: `false`
69677
69678 list
69679 ----
69680
69681 * Description: Lists commands
69682 * Usage: `list [--xml] [--raw] [--format="..."] [namespace]`
69683 * Aliases: <none>
69684
69685 The <info>list</info> command lists all commands:
69686
69687 <info>php app/console list</info>
69688
69689 You can also display the commands for a specific namespace:
69690
69691 <info>php app/console list test</info>
69692
69693 You can also output the information in other formats by using the <comment>--format</comment> option:
69694
69695 <info>php app/console list --format=xml</info>
69696
69697 It's also possible to get raw list of commands (useful for embedding command runner):
69698
69699 <info>php app/console list --raw</info>
69700
69701 ### Arguments:
69702
69703 **namespace:**
69704
69705 * Name: namespace
69706 * Is required: no
69707 * Is array: no
69708 * Description: The namespace name
69709 * Default: `NULL`
69710
69711 ### Options:
69712
69713 **xml:**
69714
69715 * Name: `--xml`
69716 * Shortcut: <none>
69717 * Accept value: no
69718 * Is value required: no
69719 * Is multiple: no
69720 * Description: To output list as XML
69721 * Default: `false`
69722
69723 **raw:**
69724
69725 * Name: `--raw`
69726 * Shortcut: <none>
69727 * Accept value: no
69728 * Is value required: no
69729 * Is multiple: no
69730 * Description: To output raw command list
69731 * Default: `false`
69732
69733 **format:**
69734
69735 * Name: `--format`
69736 * Shortcut: <none>
69737 * Accept value: yes
69738 * Is value required: yes
69739 * Is multiple: no
69740 * Description: To output list in other formats
69741 * Default: `NULL`
69742 Usage:
69743 list [--xml] [--raw] [--format="..."] [namespace]
69744
69745 Arguments:
69746 namespace The namespace name
69747
69748 Options:
69749 --xml To output list as XML
69750 --raw To output raw command list
69751 --format To output list in other formats
69752
69753 Help:
69754 The list command lists all commands:
69755
69756 php app/console list
69757
69758 You can also display the commands for a specific namespace:
69759
69760 php app/console list test
69761
69762 You can also output the information in other formats by using the --format option:
69763
69764 php app/console list --format=xml
69765
69766 It's also possible to get raw list of commands (useful for embedding command runner):
69767
69768 php app/console list --raw
69769
69770 {"arguments":{"argument_name":{"name":"argument_name","is_required":true,"is_array":false,"description":"","default":null}},"options":{"option_name":{"name":"--option_name","shortcut":"-o","accept_value":false,"is_value_required":false,"is_multiple":false,"description":"","default":false}}}
69771 <?php
69772
69773 /*
69774 * This file is part of the Symfony package.
69775 *
69776 * (c) Fabien Potencier <fabien@symfony.com>
69777 *
69778 * For the full copyright and license information, please view the LICENSE
69779 * file that was distributed with this source code.
69780 */
69781
69782 namespace Symfony\Component\Console\Tests\Fixtures;
69783
69784 use Symfony\Component\Console\Command\Command;
69785
69786 class DescriptorCommand1 extends Command
69787 {
69788 protected function configure()
69789 {
69790 $this
69791 ->setName('descriptor:command1')
69792 ->setAliases(array('alias1', 'alias2'))
69793 ->setDescription('command 1 description')
69794 ->setHelp('command 1 help')
69795 ;
69796 }
69797 }
69798
69799
69800
69801 [InvalidArgumentException]
69802 Command "foo" is not define
69803 d.
69804
69805
69806
69807 <?xml version="1.0" encoding="UTF-8"?>
69808 <definition>
69809 <arguments>
69810 <argument name="argument_name" is_required="1" is_array="0">
69811 <description></description>
69812 <defaults/>
69813 </argument>
69814 </arguments>
69815 <options/>
69816 </definition>
69817 Console Tool
69818 <?php
69819
69820 /*
69821 * This file is part of the Symfony package.
69822 *
69823 * (c) Fabien Potencier <fabien@symfony.com>
69824 *
69825 * For the full copyright and license information, please view the LICENSE
69826 * file that was distributed with this source code.
69827 */
69828
69829 namespace Symfony\Component\Console\Tests\Fixtures;
69830
69831 use Symfony\Component\Console\Application;
69832
69833 class DescriptorApplication1 extends Application
69834 {
69835 }
69836 {"arguments":[],"options":{"option_name":{"name":"--option_name","shortcut":"-o","accept_value":false,"is_value_required":false,"is_multiple":false,"description":"","default":false}}}
69837 **option_name:**
69838
69839 * Name: `--option_name`
69840 * Shortcut: `-o`
69841 * Accept value: no
69842 * Is value required: no
69843 * Is multiple: no
69844 * Description: <none>
69845 * Default: `false`
69846 <info>My Symfony application</info> version <comment>v1.0</comment>
69847
69848 <comment>Usage:</comment>
69849 [options] command [arguments]
69850
69851 <comment>Options:</comment>
69852 <info>--help</info> <info>-h</info> Display this help message.
69853 <info>--quiet</info> <info>-q</info> Do not output any message.
69854 <info>--verbose</info> <info>-v|vv|vvv</info> Increase the verbosity of messages: 1 for normal output, 2 for more verbose output and 3 for debug
69855 <info>--version</info> <info>-V</info> Display this application version.
69856 <info>--ansi</info> Force ANSI output.
69857 <info>--no-ansi</info> Disable ANSI output.
69858 <info>--no-interaction</info> <info>-n</info> Do not ask any interactive question.
69859
69860 <comment>Available commands:</comment>
69861 <info>alias1 </info> command 1 description
69862 <info>alias2 </info> command 1 description
69863 <info>help </info> Displays help for a command
69864 <info>list </info> Lists commands
69865 <comment>descriptor</comment>
69866 <info>descriptor:command1 </info> command 1 description
69867 <info>descriptor:command2 </info> command 2 description
69868 <comment>Arguments:</comment>
69869 <info>foo </info> The foo argument
69870 <info>baz </info> The baz argument<comment> (default: true)</comment>
69871 <info>bar </info> The bar argument<comment> (default: ["http://foo.com/"])</comment>
69872
69873 <comment>Options:</comment>
69874 <info>--foo</info> (-f) The foo option
69875 <info>--baz</info> The baz option<comment> (default: false)</comment>
69876 <info>--bar</info> (-b) The bar option<comment> (default: "bar")</comment>
69877 <info>--qux</info> The qux option<comment> (default: ["http://foo.com/","bar"])</comment><comment> (multiple values allowed)</comment>
69878 <info>--qux2</info> The qux2 option<comment> (default: {"foo":"bar"})</comment><comment> (multiple values allowed)</comment>
69879
69880
69881
69882 [InvalidArgumentException]
69883 Command "foo" is not defined.
69884
69885
69886
69887 **option_name:**
69888
69889 * Name: `--option_name`
69890 * Shortcut: `-o`
69891 * Accept value: yes
69892 * Is value required: no
69893 * Is multiple: yes
69894 * Description: option description
69895 * Default: `array ()`
69896 <info>--option_name</info> (-o) option description
69897 {"name":"--option_name","shortcut":"-o","accept_value":true,"is_value_required":true,"is_multiple":false,"description":"option description","default":null}
69898 <?xml version="1.0" encoding="UTF-8"?>
69899 <argument name="argument_name" is_required="0" is_array="1">
69900 <description>argument description</description>
69901 <defaults/>
69902 </argument>
69903 <?xml version="1.0" encoding="UTF-8"?>
69904 <option name="--option_name" shortcut="-o" accept_value="1" is_value_required="1" is_multiple="0">
69905 <description>option description</description>
69906 <defaults/>
69907 </option>
69908 Console Tool
69909
69910 Usage:
69911 [options] command [arguments]
69912
69913 Options:
69914 --help -h Display this help message.
69915 --quiet -q Do not output any message.
69916 --verbose -v|vv|vvv Increase the verbosity of messages: 1 for normal output, 2 for more verbose output and 3 for debug
69917 --version -V Display this application version.
69918 --ansi Force ANSI output.
69919 --no-ansi Disable ANSI output.
69920 --no-interaction -n Do not ask any interactive question.
69921
69922 Available commands:
69923 help Displays help for a command
69924 list Lists commands
69925 <comment>Arguments:</comment>
69926 <info>argument_name </info>
69927
69928 <comment>Options:</comment>
69929 <info>--option_name</info> (-o)
69930 <?xml version="1.0" encoding="UTF-8"?>
69931 <option name="--option_name" shortcut="-o" accept_value="1" is_value_required="0" is_multiple="0">
69932 <description>option description</description>
69933 <defaults>
69934 <default>default_value</default>
69935 </defaults>
69936 </option>
69937 <info>Console Tool</info>
69938
69939 <comment>Usage:</comment>
69940 [options] command [arguments]
69941
69942 <comment>Options:</comment>
69943 <info>--help</info> <info>-h</info> Display this help message.
69944 <info>--quiet</info> <info>-q</info> Do not output any message.
69945 <info>--verbose</info> <info>-v|vv|vvv</info> Increase the verbosity of messages: 1 for normal output, 2 for more verbose output and 3 for debug
69946 <info>--version</info> <info>-V</info> Display this application version.
69947 <info>--ansi</info> Force ANSI output.
69948 <info>--no-ansi</info> Disable ANSI output.
69949 <info>--no-interaction</info> <info>-n</info> Do not ask any interactive question.
69950
69951 <comment>Available commands:</comment>
69952 <info>help </info> Displays help for a command
69953 <info>list </info> Lists commands
69954 <comment>Usage:</comment>
69955 namespace:name
69956
69957 <comment>Aliases:</comment> <info>name</info>
69958 <comment>Arguments:</comment>
69959 <info>command </info> The command to execute
69960
69961 <comment>Options:</comment>
69962 <info>--help</info> (-h) Display this help message.
69963 <info>--quiet</info> (-q) Do not output any message.
69964 <info>--verbose</info> (-v|vv|vvv) Increase the verbosity of messages: 1 for normal output, 2 for more verbose output and 3 for debug
69965 <info>--version</info> (-V) Display this application version.
69966 <info>--ansi</info> Force ANSI output.
69967 <info>--no-ansi</info> Disable ANSI output.
69968 <info>--no-interaction</info> (-n) Do not ask any interactive question.
69969
69970 <comment>Help:</comment>
69971 help
69972 <?xml version="1.0" encoding="UTF-8"?>
69973 <command id="descriptor:command1" name="descriptor:command1">
69974 <usage>descriptor:command1</usage>
69975 <description>command 1 description</description>
69976 <help>command 1 help</help>
69977 <aliases>
69978 <alias>alias1</alias>
69979 <alias>alias2</alias>
69980 </aliases>
69981 <arguments/>
69982 <options/>
69983 </command>
69984 ### Arguments:
69985
69986 **argument_name:**
69987
69988 * Name: argument_name
69989 * Is required: yes
69990 * Is array: no
69991 * Description: <none>
69992 * Default: `NULL`
69993 <?php
69994
69995 /*
69996 * This file is part of the Symfony package.
69997 *
69998 * (c) Fabien Potencier <fabien@symfony.com>
69999 *
70000 * For the full copyright and license information, please view the LICENSE
70001 * file that was distributed with this source code.
70002 */
70003
70004 namespace Symfony\Component\Console\Tests\Fixtures;
70005
70006 use Symfony\Component\Console\Command\Command;
70007 use Symfony\Component\Console\Input\InputArgument;
70008 use Symfony\Component\Console\Input\InputOption;
70009
70010 class DescriptorCommand2 extends Command
70011 {
70012 protected function configure()
70013 {
70014 $this
70015 ->setName('descriptor:command2')
70016 ->setDescription('command 2 description')
70017 ->setHelp('command 2 help')
70018 ->addArgument('argument_name', InputArgument::REQUIRED)
70019 ->addOption('option_name', 'o', InputOption::VALUE_NONE)
70020 ;
70021 }
70022 }
70023 **option_name:**
70024
70025 * Name: `--option_name`
70026 * Shortcut: `-o`
70027 * Accept value: yes
70028 * Is value required: no
70029 * Is multiple: no
70030 * Description: option description
70031 * Default: `'default_value'`
70032 <comment>Arguments:</comment>
70033 <info>argument_name </info>
70034 {"arguments":[],"options":[]}
70035 <?xml version="1.0" encoding="UTF-8"?>
70036 <option name="--option_name" shortcut="-o" accept_value="0" is_value_required="0" is_multiple="0">
70037 <description></description>
70038 </option>
70039 <?xml version="1.0" encoding="UTF-8"?>
70040 <symfony>
70041 <commands>
70042 <command id="help" name="help">
70043 <usage>help [--xml] [--format="..."] [--raw] [command_name]</usage>
70044 <description>Displays help for a command</description>
70045 <help>The &lt;info&gt;help&lt;/info&gt; command displays help for a given command:
70046
70047 &lt;info&gt;php app/console help list&lt;/info&gt;
70048
70049 You can also output the help in other formats by using the &lt;comment&gt;--format&lt;/comment&gt; option:
70050
70051 &lt;info&gt;php app/console help --format=xml list&lt;/info&gt;
70052
70053 To display the list of available commands, please use the &lt;info&gt;list&lt;/info&gt; command.</help>
70054 <aliases/>
70055 <arguments>
70056 <argument name="command_name" is_required="0" is_array="0">
70057 <description>The command name</description>
70058 <defaults>
70059 <default>help</default>
70060 </defaults>
70061 </argument>
70062 </arguments>
70063 <options>
70064 <option name="--xml" shortcut="" accept_value="0" is_value_required="0" is_multiple="0">
70065 <description>To output help as XML</description>
70066 </option>
70067 <option name="--format" shortcut="" accept_value="1" is_value_required="1" is_multiple="0">
70068 <description>To output help in other formats</description>
70069 <defaults/>
70070 </option>
70071 <option name="--raw" shortcut="" accept_value="0" is_value_required="0" is_multiple="0">
70072 <description>To output raw command help</description>
70073 </option>
70074 <option name="--help" shortcut="-h" accept_value="0" is_value_required="0" is_multiple="0">
70075 <description>Display this help message.</description>
70076 </option>
70077 <option name="--quiet" shortcut="-q" accept_value="0" is_value_required="0" is_multiple="0">
70078 <description>Do not output any message.</description>
70079 </option>
70080 <option name="--verbose" shortcut="-v" shortcuts="-v|-vv|-vvv" accept_value="0" is_value_required="0" is_multiple="0">
70081 <description>Increase the verbosity of messages: 1 for normal output, 2 for more verbose output and 3 for debug</description>
70082 </option>
70083 <option name="--version" shortcut="-V" accept_value="0" is_value_required="0" is_multiple="0">
70084 <description>Display this application version.</description>
70085 </option>
70086 <option name="--ansi" shortcut="" accept_value="0" is_value_required="0" is_multiple="0">
70087 <description>Force ANSI output.</description>
70088 </option>
70089 <option name="--no-ansi" shortcut="" accept_value="0" is_value_required="0" is_multiple="0">
70090 <description>Disable ANSI output.</description>
70091 </option>
70092 <option name="--no-interaction" shortcut="-n" accept_value="0" is_value_required="0" is_multiple="0">
70093 <description>Do not ask any interactive question.</description>
70094 </option>
70095 </options>
70096 </command>
70097 <command id="list" name="list">
70098 <usage>list [--xml] [--raw] [--format="..."] [namespace]</usage>
70099 <description>Lists commands</description>
70100 <help>The &lt;info&gt;list&lt;/info&gt; command lists all commands:
70101
70102 &lt;info&gt;php app/console list&lt;/info&gt;
70103
70104 You can also display the commands for a specific namespace:
70105
70106 &lt;info&gt;php app/console list test&lt;/info&gt;
70107
70108 You can also output the information in other formats by using the &lt;comment&gt;--format&lt;/comment&gt; option:
70109
70110 &lt;info&gt;php app/console list --format=xml&lt;/info&gt;
70111
70112 It's also possible to get raw list of commands (useful for embedding command runner):
70113
70114 &lt;info&gt;php app/console list --raw&lt;/info&gt;</help>
70115 <aliases/>
70116 <arguments>
70117 <argument name="namespace" is_required="0" is_array="0">
70118 <description>The namespace name</description>
70119 <defaults/>
70120 </argument>
70121 </arguments>
70122 <options>
70123 <option name="--xml" shortcut="" accept_value="0" is_value_required="0" is_multiple="0">
70124 <description>To output list as XML</description>
70125 </option>
70126 <option name="--raw" shortcut="" accept_value="0" is_value_required="0" is_multiple="0">
70127 <description>To output raw command list</description>
70128 </option>
70129 <option name="--format" shortcut="" accept_value="1" is_value_required="1" is_multiple="0">
70130 <description>To output list in other formats</description>
70131 <defaults/>
70132 </option>
70133 </options>
70134 </command>
70135 </commands>
70136 <namespaces>
70137 <namespace id="_global">
70138 <command>help</command>
70139 <command>list</command>
70140 </namespace>
70141 </namespaces>
70142 </symfony>
70143 <?php
70144
70145 use Symfony\Component\Console\Command\Command;
70146 use Symfony\Component\Console\Input\InputInterface;
70147 use Symfony\Component\Console\Output\OutputInterface;
70148
70149 class Foo3Command extends Command
70150 {
70151 protected function configure()
70152 {
70153 $this
70154 ->setName('foo3:bar')
70155 ->setDescription('The foo3:bar command')
70156 ;
70157 }
70158
70159 protected function execute(InputInterface $input, OutputInterface $output)
70160 {
70161 try {
70162 throw new \Exception("First exception");
70163 } catch (\Exception $e) {
70164 throw new \Exception("Second exception", 0, $e);
70165 }
70166 }
70167 }
70168 <info>Console Tool</info>
70169
70170 <comment>Usage:</comment>
70171 [options] command [arguments]
70172
70173 <comment>Options:</comment>
70174 <info>--help</info> <info>-h</info> Display this help message.
70175 <info>--quiet</info> <info>-q</info> Do not output any message.
70176 <info>--verbose</info> <info>-v|vv|vvv</info> Increase the verbosity of messages: 1 for normal output, 2 for more verbose output and 3 for debug
70177 <info>--version</info> <info>-V</info> Display this application version.
70178 <info>--ansi</info> Force ANSI output.
70179 <info>--no-ansi</info> Disable ANSI output.
70180 <info>--no-interaction</info> <info>-n</info> Do not ask any interactive question.
70181
70182 <comment>Available commands for the "foo" namespace:</comment>
70183 <info>foo:bar </info> The foo:bar command<?xml version="1.0" encoding="UTF-8"?>
70184 <command id="namespace:name" name="namespace:name">
70185 <usage>namespace:name</usage>
70186 <description>description</description>
70187 <help>help</help>
70188 <aliases>
70189 <alias>name</alias>
70190 </aliases>
70191 <arguments>
70192 <argument name="command" is_required="1" is_array="0">
70193 <description>The command to execute</description>
70194 <defaults/>
70195 </argument>
70196 </arguments>
70197 <options>
70198 <option name="--help" shortcut="-h" accept_value="0" is_value_required="0" is_multiple="0">
70199 <description>Display this help message.</description>
70200 </option>
70201 <option name="--quiet" shortcut="-q" accept_value="0" is_value_required="0" is_multiple="0">
70202 <description>Do not output any message.</description>
70203 </option>
70204 <option name="--verbose" shortcut="-v" shortcuts="-v|-vv|-vvv" accept_value="0" is_value_required="0" is_multiple="0">
70205 <description>Increase the verbosity of messages: 1 for normal output, 2 for more verbose output and 3 for debug</description>
70206 </option>
70207 <option name="--version" shortcut="-V" accept_value="0" is_value_required="0" is_multiple="0">
70208 <description>Display this application version.</description>
70209 </option>
70210 <option name="--ansi" shortcut="" accept_value="0" is_value_required="0" is_multiple="0">
70211 <description>Force ANSI output.</description>
70212 </option>
70213 <option name="--no-ansi" shortcut="" accept_value="0" is_value_required="0" is_multiple="0">
70214 <description>Disable ANSI output.</description>
70215 </option>
70216 <option name="--no-interaction" shortcut="-n" accept_value="0" is_value_required="0" is_multiple="0">
70217 <description>Do not ask any interactive question.</description>
70218 </option>
70219 </options>
70220 </command>
70221 <?php
70222
70223 use Symfony\Component\Console\Command\Command;
70224 use Symfony\Component\Console\Input\InputInterface;
70225 use Symfony\Component\Console\Output\OutputInterface;
70226
70227 class FooCommand extends Command
70228 {
70229 public $input;
70230 public $output;
70231
70232 protected function configure()
70233 {
70234 $this
70235 ->setName('foo:bar')
70236 ->setDescription('The foo:bar command')
70237 ->setAliases(array('afoobar'))
70238 ;
70239 }
70240
70241 protected function interact(InputInterface $input, OutputInterface $output)
70242 {
70243 $output->writeln('interact called');
70244 }
70245
70246 protected function execute(InputInterface $input, OutputInterface $output)
70247 {
70248 $this->input = $input;
70249 $this->output = $output;
70250
70251 $output->writeln('called');
70252 }
70253 }
70254 descriptor:command1
70255 -------------------
70256
70257 * Description: command 1 description
70258 * Usage: `descriptor:command1`
70259 * Aliases: `alias1`, `alias2`
70260
70261 command 1 help
70262 My Symfony application
70263 ======================
70264
70265 * alias1
70266 * alias2
70267 * help
70268 * list
70269
70270 **descriptor:**
70271
70272 * descriptor:command1
70273 * descriptor:command2
70274
70275 help
70276 ----
70277
70278 * Description: Displays help for a command
70279 * Usage: `help [--xml] [--format="..."] [--raw] [command_name]`
70280 * Aliases: <none>
70281
70282 The <info>help</info> command displays help for a given command:
70283
70284 <info>php app/console help list</info>
70285
70286 You can also output the help in other formats by using the <comment>--format</comment> option:
70287
70288 <info>php app/console help --format=xml list</info>
70289
70290 To display the list of available commands, please use the <info>list</info> command.
70291
70292 ### Arguments:
70293
70294 **command_name:**
70295
70296 * Name: command_name
70297 * Is required: no
70298 * Is array: no
70299 * Description: The command name
70300 * Default: `'help'`
70301
70302 ### Options:
70303
70304 **xml:**
70305
70306 * Name: `--xml`
70307 * Shortcut: <none>
70308 * Accept value: no
70309 * Is value required: no
70310 * Is multiple: no
70311 * Description: To output help as XML
70312 * Default: `false`
70313
70314 **format:**
70315
70316 * Name: `--format`
70317 * Shortcut: <none>
70318 * Accept value: yes
70319 * Is value required: yes
70320 * Is multiple: no
70321 * Description: To output help in other formats
70322 * Default: `NULL`
70323
70324 **raw:**
70325
70326 * Name: `--raw`
70327 * Shortcut: <none>
70328 * Accept value: no
70329 * Is value required: no
70330 * Is multiple: no
70331 * Description: To output raw command help
70332 * Default: `false`
70333
70334 **help:**
70335
70336 * Name: `--help`
70337 * Shortcut: `-h`
70338 * Accept value: no
70339 * Is value required: no
70340 * Is multiple: no
70341 * Description: Display this help message.
70342 * Default: `false`
70343
70344 **quiet:**
70345
70346 * Name: `--quiet`
70347 * Shortcut: `-q`
70348 * Accept value: no
70349 * Is value required: no
70350 * Is multiple: no
70351 * Description: Do not output any message.
70352 * Default: `false`
70353
70354 **verbose:**
70355
70356 * Name: `--verbose`
70357 * Shortcut: `-v|-vv|-vvv`
70358 * Accept value: no
70359 * Is value required: no
70360 * Is multiple: no
70361 * Description: Increase the verbosity of messages: 1 for normal output, 2 for more verbose output and 3 for debug
70362 * Default: `false`
70363
70364 **version:**
70365
70366 * Name: `--version`
70367 * Shortcut: `-V`
70368 * Accept value: no
70369 * Is value required: no
70370 * Is multiple: no
70371 * Description: Display this application version.
70372 * Default: `false`
70373
70374 **ansi:**
70375
70376 * Name: `--ansi`
70377 * Shortcut: <none>
70378 * Accept value: no
70379 * Is value required: no
70380 * Is multiple: no
70381 * Description: Force ANSI output.
70382 * Default: `false`
70383
70384 **no-ansi:**
70385
70386 * Name: `--no-ansi`
70387 * Shortcut: <none>
70388 * Accept value: no
70389 * Is value required: no
70390 * Is multiple: no
70391 * Description: Disable ANSI output.
70392 * Default: `false`
70393
70394 **no-interaction:**
70395
70396 * Name: `--no-interaction`
70397 * Shortcut: `-n`
70398 * Accept value: no
70399 * Is value required: no
70400 * Is multiple: no
70401 * Description: Do not ask any interactive question.
70402 * Default: `false`
70403
70404 list
70405 ----
70406
70407 * Description: Lists commands
70408 * Usage: `list [--xml] [--raw] [--format="..."] [namespace]`
70409 * Aliases: <none>
70410
70411 The <info>list</info> command lists all commands:
70412
70413 <info>php app/console list</info>
70414
70415 You can also display the commands for a specific namespace:
70416
70417 <info>php app/console list test</info>
70418
70419 You can also output the information in other formats by using the <comment>--format</comment> option:
70420
70421 <info>php app/console list --format=xml</info>
70422
70423 It's also possible to get raw list of commands (useful for embedding command runner):
70424
70425 <info>php app/console list --raw</info>
70426
70427 ### Arguments:
70428
70429 **namespace:**
70430
70431 * Name: namespace
70432 * Is required: no
70433 * Is array: no
70434 * Description: The namespace name
70435 * Default: `NULL`
70436
70437 ### Options:
70438
70439 **xml:**
70440
70441 * Name: `--xml`
70442 * Shortcut: <none>
70443 * Accept value: no
70444 * Is value required: no
70445 * Is multiple: no
70446 * Description: To output list as XML
70447 * Default: `false`
70448
70449 **raw:**
70450
70451 * Name: `--raw`
70452 * Shortcut: <none>
70453 * Accept value: no
70454 * Is value required: no
70455 * Is multiple: no
70456 * Description: To output raw command list
70457 * Default: `false`
70458
70459 **format:**
70460
70461 * Name: `--format`
70462 * Shortcut: <none>
70463 * Accept value: yes
70464 * Is value required: yes
70465 * Is multiple: no
70466 * Description: To output list in other formats
70467 * Default: `NULL`
70468
70469 descriptor:command1
70470 -------------------
70471
70472 * Description: command 1 description
70473 * Usage: `descriptor:command1`
70474 * Aliases: `alias1`, `alias2`
70475
70476 command 1 help
70477
70478 ### Options:
70479
70480 **help:**
70481
70482 * Name: `--help`
70483 * Shortcut: `-h`
70484 * Accept value: no
70485 * Is value required: no
70486 * Is multiple: no
70487 * Description: Display this help message.
70488 * Default: `false`
70489
70490 **quiet:**
70491
70492 * Name: `--quiet`
70493 * Shortcut: `-q`
70494 * Accept value: no
70495 * Is value required: no
70496 * Is multiple: no
70497 * Description: Do not output any message.
70498 * Default: `false`
70499
70500 **verbose:**
70501
70502 * Name: `--verbose`
70503 * Shortcut: `-v|-vv|-vvv`
70504 * Accept value: no
70505 * Is value required: no
70506 * Is multiple: no
70507 * Description: Increase the verbosity of messages: 1 for normal output, 2 for more verbose output and 3 for debug
70508 * Default: `false`
70509
70510 **version:**
70511
70512 * Name: `--version`
70513 * Shortcut: `-V`
70514 * Accept value: no
70515 * Is value required: no
70516 * Is multiple: no
70517 * Description: Display this application version.
70518 * Default: `false`
70519
70520 **ansi:**
70521
70522 * Name: `--ansi`
70523 * Shortcut: <none>
70524 * Accept value: no
70525 * Is value required: no
70526 * Is multiple: no
70527 * Description: Force ANSI output.
70528 * Default: `false`
70529
70530 **no-ansi:**
70531
70532 * Name: `--no-ansi`
70533 * Shortcut: <none>
70534 * Accept value: no
70535 * Is value required: no
70536 * Is multiple: no
70537 * Description: Disable ANSI output.
70538 * Default: `false`
70539
70540 **no-interaction:**
70541
70542 * Name: `--no-interaction`
70543 * Shortcut: `-n`
70544 * Accept value: no
70545 * Is value required: no
70546 * Is multiple: no
70547 * Description: Do not ask any interactive question.
70548 * Default: `false`
70549
70550 descriptor:command2
70551 -------------------
70552
70553 * Description: command 2 description
70554 * Usage: `descriptor:command2 [-o|--option_name] argument_name`
70555 * Aliases: <none>
70556
70557 command 2 help
70558
70559 ### Arguments:
70560
70561 **argument_name:**
70562
70563 * Name: argument_name
70564 * Is required: yes
70565 * Is array: no
70566 * Description: <none>
70567 * Default: `NULL`
70568
70569 ### Options:
70570
70571 **option_name:**
70572
70573 * Name: `--option_name`
70574 * Shortcut: `-o`
70575 * Accept value: no
70576 * Is value required: no
70577 * Is multiple: no
70578 * Description: <none>
70579 * Default: `false`
70580
70581 **help:**
70582
70583 * Name: `--help`
70584 * Shortcut: `-h`
70585 * Accept value: no
70586 * Is value required: no
70587 * Is multiple: no
70588 * Description: Display this help message.
70589 * Default: `false`
70590
70591 **quiet:**
70592
70593 * Name: `--quiet`
70594 * Shortcut: `-q`
70595 * Accept value: no
70596 * Is value required: no
70597 * Is multiple: no
70598 * Description: Do not output any message.
70599 * Default: `false`
70600
70601 **verbose:**
70602
70603 * Name: `--verbose`
70604 * Shortcut: `-v|-vv|-vvv`
70605 * Accept value: no
70606 * Is value required: no
70607 * Is multiple: no
70608 * Description: Increase the verbosity of messages: 1 for normal output, 2 for more verbose output and 3 for debug
70609 * Default: `false`
70610
70611 **version:**
70612
70613 * Name: `--version`
70614 * Shortcut: `-V`
70615 * Accept value: no
70616 * Is value required: no
70617 * Is multiple: no
70618 * Description: Display this application version.
70619 * Default: `false`
70620
70621 **ansi:**
70622
70623 * Name: `--ansi`
70624 * Shortcut: <none>
70625 * Accept value: no
70626 * Is value required: no
70627 * Is multiple: no
70628 * Description: Force ANSI output.
70629 * Default: `false`
70630
70631 **no-ansi:**
70632
70633 * Name: `--no-ansi`
70634 * Shortcut: <none>
70635 * Accept value: no
70636 * Is value required: no
70637 * Is multiple: no
70638 * Description: Disable ANSI output.
70639 * Default: `false`
70640
70641 **no-interaction:**
70642
70643 * Name: `--no-interaction`
70644 * Shortcut: `-n`
70645 * Accept value: no
70646 * Is value required: no
70647 * Is multiple: no
70648 * Description: Do not ask any interactive question.
70649 * Default: `false`
70650 {"name":"--option_name","shortcut":"-o","accept_value":false,"is_value_required":false,"is_multiple":false,"description":"","default":false}
70651 <comment>Usage:</comment>
70652 descriptor:command1
70653
70654 <comment>Aliases:</comment> <info>alias1, alias2</info>
70655
70656 <comment>Help:</comment>
70657 command 1 help
70658 <?php
70659
70660 /*
70661 * This file is part of the Symfony package.
70662 *
70663 * (c) Fabien Potencier <fabien@symfony.com>
70664 *
70665 * For the full copyright and license information, please view the LICENSE
70666 * file that was distributed with this source code.
70667 */
70668
70669 namespace Symfony\Component\Console\Tests;
70670
70671 use Symfony\Component\Console\Application;
70672 use Symfony\Component\Console\Helper\HelperSet;
70673 use Symfony\Component\Console\Helper\FormatterHelper;
70674 use Symfony\Component\Console\Input\ArrayInput;
70675 use Symfony\Component\Console\Input\InputInterface;
70676 use Symfony\Component\Console\Input\InputArgument;
70677 use Symfony\Component\Console\Input\InputDefinition;
70678 use Symfony\Component\Console\Input\InputOption;
70679 use Symfony\Component\Console\Output\NullOutput;
70680 use Symfony\Component\Console\Output\Output;
70681 use Symfony\Component\Console\Output\OutputInterface;
70682 use Symfony\Component\Console\Tester\ApplicationTester;
70683 use Symfony\Component\Console\Event\ConsoleCommandEvent;
70684 use Symfony\Component\Console\Event\ConsoleExceptionEvent;
70685 use Symfony\Component\Console\Event\ConsoleTerminateEvent;
70686 use Symfony\Component\EventDispatcher\EventDispatcher;
70687
70688 class ApplicationTest extends \PHPUnit_Framework_TestCase
70689 {
70690 protected static $fixturesPath;
70691
70692 public static function setUpBeforeClass()
70693 {
70694 self::$fixturesPath = realpath(__DIR__.'/Fixtures/');
70695 require_once self::$fixturesPath.'/FooCommand.php';
70696 require_once self::$fixturesPath.'/Foo1Command.php';
70697 require_once self::$fixturesPath.'/Foo2Command.php';
70698 require_once self::$fixturesPath.'/Foo3Command.php';
70699 require_once self::$fixturesPath.'/Foo4Command.php';
70700 }
70701
70702 protected function normalizeLineBreaks($text)
70703 {
70704 return str_replace(PHP_EOL, "\n", $text);
70705 }
70706
70707 /**
70708 * Replaces the dynamic placeholders of the command help text with a static version.
70709 * The placeholder %command.full_name% includes the script path that is not predictable
70710 * and can not be tested against.
70711 */
70712 protected function ensureStaticCommandHelp(Application $application)
70713 {
70714 foreach ($application->all() as $command) {
70715 $command->setHelp(str_replace('%command.full_name%', 'app/console %command.name%', $command->getHelp()));
70716 }
70717 }
70718
70719 public function testConstructor()
70720 {
70721 $application = new Application('foo', 'bar');
70722 $this->assertEquals('foo', $application->getName(), '__construct() takes the application name as its first argument');
70723 $this->assertEquals('bar', $application->getVersion(), '__construct() takes the application version as its second argument');
70724 $this->assertEquals(array('help', 'list'), array_keys($application->all()), '__construct() registered the help and list commands by default');
70725 }
70726
70727 public function testSetGetName()
70728 {
70729 $application = new Application();
70730 $application->setName('foo');
70731 $this->assertEquals('foo', $application->getName(), '->setName() sets the name of the application');
70732 }
70733
70734 public function testSetGetVersion()
70735 {
70736 $application = new Application();
70737 $application->setVersion('bar');
70738 $this->assertEquals('bar', $application->getVersion(), '->setVersion() sets the version of the application');
70739 }
70740
70741 public function testGetLongVersion()
70742 {
70743 $application = new Application('foo', 'bar');
70744 $this->assertEquals('<info>foo</info> version <comment>bar</comment>', $application->getLongVersion(), '->getLongVersion() returns the long version of the application');
70745 }
70746
70747 public function testHelp()
70748 {
70749 $application = new Application();
70750 $this->assertStringEqualsFile(self::$fixturesPath.'/application_gethelp.txt', $this->normalizeLineBreaks($application->getHelp()), '->setHelp() returns a help message');
70751 }
70752
70753 public function testAll()
70754 {
70755 $application = new Application();
70756 $commands = $application->all();
70757 $this->assertEquals('Symfony\\Component\\Console\\Command\\HelpCommand', get_class($commands['help']), '->all() returns the registered commands');
70758
70759 $application->add(new \FooCommand());
70760 $commands = $application->all('foo');
70761 $this->assertEquals(1, count($commands), '->all() takes a namespace as its first argument');
70762 }
70763
70764 public function testRegister()
70765 {
70766 $application = new Application();
70767 $command = $application->register('foo');
70768 $this->assertEquals('foo', $command->getName(), '->register() registers a new command');
70769 }
70770
70771 public function testAdd()
70772 {
70773 $application = new Application();
70774 $application->add($foo = new \FooCommand());
70775 $commands = $application->all();
70776 $this->assertEquals($foo, $commands['foo:bar'], '->add() registers a command');
70777
70778 $application = new Application();
70779 $application->addCommands(array($foo = new \FooCommand(), $foo1 = new \Foo1Command()));
70780 $commands = $application->all();
70781 $this->assertEquals(array($foo, $foo1), array($commands['foo:bar'], $commands['foo:bar1']), '->addCommands() registers an array of commands');
70782 }
70783
70784 public function testHasGet()
70785 {
70786 $application = new Application();
70787 $this->assertTrue($application->has('list'), '->has() returns true if a named command is registered');
70788 $this->assertFalse($application->has('afoobar'), '->has() returns false if a named command is not registered');
70789
70790 $application->add($foo = new \FooCommand());
70791 $this->assertTrue($application->has('afoobar'), '->has() returns true if an alias is registered');
70792 $this->assertEquals($foo, $application->get('foo:bar'), '->get() returns a command by name');
70793 $this->assertEquals($foo, $application->get('afoobar'), '->get() returns a command by alias');
70794
70795 $application = new Application();
70796 $application->add($foo = new \FooCommand());
70797 // simulate --help
70798 $r = new \ReflectionObject($application);
70799 $p = $r->getProperty('wantHelps');
70800 $p->setAccessible(true);
70801 $p->setValue($application, true);
70802 $command = $application->get('foo:bar');
70803 $this->assertInstanceOf('Symfony\Component\Console\Command\HelpCommand', $command, '->get() returns the help command if --help is provided as the input');
70804 }
70805
70806 public function testSilentHelp()
70807 {
70808 $application = new Application();
70809 $application->setAutoExit(false);
70810 $application->setCatchExceptions(false);
70811
70812 $tester = new ApplicationTester($application);
70813 $tester->run(array('-h' => true, '-q' => true), array('decorated' => false));
70814
70815 $this->assertEmpty($tester->getDisplay(true));
70816 }
70817
70818 /**
70819 * @expectedException \InvalidArgumentException
70820 * @expectedExceptionMessage The command "foofoo" does not exist.
70821 */
70822 public function testGetInvalidCommand()
70823 {
70824 $application = new Application();
70825 $application->get('foofoo');
70826 }
70827
70828 public function testGetNamespaces()
70829 {
70830 $application = new Application();
70831 $application->add(new \FooCommand());
70832 $application->add(new \Foo1Command());
70833 $this->assertEquals(array('foo'), $application->getNamespaces(), '->getNamespaces() returns an array of unique used namespaces');
70834 }
70835
70836 public function testFindNamespace()
70837 {
70838 $application = new Application();
70839 $application->add(new \FooCommand());
70840 $this->assertEquals('foo', $application->findNamespace('foo'), '->findNamespace() returns the given namespace if it exists');
70841 $this->assertEquals('foo', $application->findNamespace('f'), '->findNamespace() finds a namespace given an abbreviation');
70842 $application->add(new \Foo2Command());
70843 $this->assertEquals('foo', $application->findNamespace('foo'), '->findNamespace() returns the given namespace if it exists');
70844 }
70845
70846 /**
70847 * @expectedException \InvalidArgumentException
70848 * @expectedExceptionMessage The namespace "f" is ambiguous (foo, foo1).
70849 */
70850 public function testFindAmbiguousNamespace()
70851 {
70852 $application = new Application();
70853 $application->add(new \FooCommand());
70854 $application->add(new \Foo2Command());
70855 $application->findNamespace('f');
70856 }
70857
70858 /**
70859 * @expectedException \InvalidArgumentException
70860 * @expectedExceptionMessage There are no commands defined in the "bar" namespace.
70861 */
70862 public function testFindInvalidNamespace()
70863 {
70864 $application = new Application();
70865 $application->findNamespace('bar');
70866 }
70867
70868 public function testFind()
70869 {
70870 $application = new Application();
70871 $application->add(new \FooCommand());
70872
70873 $this->assertInstanceOf('FooCommand', $application->find('foo:bar'), '->find() returns a command if its name exists');
70874 $this->assertInstanceOf('Symfony\Component\Console\Command\HelpCommand', $application->find('h'), '->find() returns a command if its name exists');
70875 $this->assertInstanceOf('FooCommand', $application->find('f:bar'), '->find() returns a command if the abbreviation for the namespace exists');
70876 $this->assertInstanceOf('FooCommand', $application->find('f:b'), '->find() returns a command if the abbreviation for the namespace and the command name exist');
70877 $this->assertInstanceOf('FooCommand', $application->find('a'), '->find() returns a command if the abbreviation exists for an alias');
70878 }
70879
70880 /**
70881 * @dataProvider provideAmbiguousAbbreviations
70882 */
70883 public function testFindWithAmbiguousAbbreviations($abbreviation, $expectedExceptionMessage)
70884 {
70885 $this->setExpectedException('InvalidArgumentException', $expectedExceptionMessage);
70886
70887 $application = new Application();
70888 $application->add(new \FooCommand());
70889 $application->add(new \Foo1Command());
70890 $application->add(new \Foo2Command());
70891
70892 $application->find($abbreviation);
70893 }
70894
70895 public function provideAmbiguousAbbreviations()
70896 {
70897 return array(
70898 array('f', 'Command "f" is not defined.'),
70899 array('a', 'Command "a" is ambiguous (afoobar, afoobar1 and 1 more).'),
70900 array('foo:b', 'Command "foo:b" is ambiguous (foo:bar, foo:bar1).')
70901 );
70902 }
70903
70904 public function testFindCommandEqualNamespace()
70905 {
70906 $application = new Application();
70907 $application->add(new \Foo3Command());
70908 $application->add(new \Foo4Command());
70909
70910 $this->assertInstanceOf('Foo3Command', $application->find('foo3:bar'), '->find() returns the good command even if a namespace has same name');
70911 $this->assertInstanceOf('Foo4Command', $application->find('foo3:bar:toh'), '->find() returns a command even if its namespace equals another command name');
70912 }
70913
70914 /**
70915 * @dataProvider provideInvalidCommandNamesSingle
70916 * @expectedException \InvalidArgumentException
70917 * @expectedExceptionMessage Did you mean this
70918 */
70919 public function testFindAlternativeExceptionMessageSingle($name)
70920 {
70921 $application = new Application();
70922 $application->add(new \FooCommand());
70923 $application->find($name);
70924 }
70925
70926 public function provideInvalidCommandNamesSingle()
70927 {
70928 return array(
70929 array('foo:baR'),
70930 array('foO:bar')
70931 );
70932 }
70933
70934 public function testFindAlternativeExceptionMessageMultiple()
70935 {
70936 $application = new Application();
70937 $application->add(new \FooCommand());
70938 $application->add(new \Foo1Command());
70939 $application->add(new \Foo2Command());
70940
70941 // Command + plural
70942 try {
70943 $application->find('foo:baR');
70944 $this->fail('->find() throws an \InvalidArgumentException if command does not exist, with alternatives');
70945 } catch (\Exception $e) {
70946 $this->assertInstanceOf('\InvalidArgumentException', $e, '->find() throws an \InvalidArgumentException if command does not exist, with alternatives');
70947 $this->assertRegExp('/Did you mean one of these/', $e->getMessage(), '->find() throws an \InvalidArgumentException if command does not exist, with alternatives');
70948 }
70949
70950 // Namespace + plural
70951 try {
70952 $application->find('foo2:bar');
70953 $this->fail('->find() throws an \InvalidArgumentException if command does not exist, with alternatives');
70954 } catch (\Exception $e) {
70955 $this->assertInstanceOf('\InvalidArgumentException', $e, '->find() throws an \InvalidArgumentException if command does not exist, with alternatives');
70956 $this->assertRegExp('/Did you mean one of these/', $e->getMessage(), '->find() throws an \InvalidArgumentException if command does not exist, with alternatives');
70957 }
70958
70959 $application->add(new \Foo3Command());
70960 $application->add(new \Foo4Command());
70961
70962 // Subnamespace + plural
70963 try {
70964 $a = $application->find('foo3:');
70965 $this->fail('->find() should throw an \InvalidArgumentException if a command is ambiguous because of a subnamespace, with alternatives');
70966 } catch (\Exception $e) {
70967 $this->assertInstanceOf('\InvalidArgumentException', $e);
70968 $this->assertRegExp('/foo3:bar/', $e->getMessage());
70969 $this->assertRegExp('/foo3:bar:toh/', $e->getMessage());
70970 }
70971 }
70972
70973 public function testFindAlternativeCommands()
70974 {
70975 $application = new Application();
70976
70977 $application->add(new \FooCommand());
70978 $application->add(new \Foo1Command());
70979 $application->add(new \Foo2Command());
70980
70981 try {
70982 $application->find($commandName = 'Unknown command');
70983 $this->fail('->find() throws an \InvalidArgumentException if command does not exist');
70984 } catch (\Exception $e) {
70985 $this->assertInstanceOf('\InvalidArgumentException', $e, '->find() throws an \InvalidArgumentException if command does not exist');
70986 $this->assertEquals(sprintf('Command "%s" is not defined.', $commandName), $e->getMessage(), '->find() throws an \InvalidArgumentException if command does not exist, without alternatives');
70987 }
70988
70989 try {
70990 $application->find($commandName = 'foo');
70991 $this->fail('->find() throws an \InvalidArgumentException if command does not exist');
70992 } catch (\Exception $e) {
70993 $this->assertInstanceOf('\InvalidArgumentException', $e, '->find() throws an \InvalidArgumentException if command does not exist');
70994 $this->assertRegExp(sprintf('/Command "%s" is not defined./', $commandName), $e->getMessage(), '->find() throws an \InvalidArgumentException if command does not exist, with alternatives');
70995 $this->assertRegExp('/foo:bar/', $e->getMessage(), '->find() throws an \InvalidArgumentException if command does not exist, with alternative : "foo:bar"');
70996 $this->assertRegExp('/foo1:bar/', $e->getMessage(), '->find() throws an \InvalidArgumentException if command does not exist, with alternative : "foo1:bar"');
70997 $this->assertRegExp('/foo:bar1/', $e->getMessage(), '->find() throws an \InvalidArgumentException if command does not exist, with alternative : "foo:bar1"');
70998 }
70999
71000 // Test if "foo1" command throw an "\InvalidArgumentException" and does not contain
71001 // "foo:bar" as alternative because "foo1" is too far from "foo:bar"
71002 try {
71003 $application->find($commandName = 'foo1');
71004 $this->fail('->find() throws an \InvalidArgumentException if command does not exist');
71005 } catch (\Exception $e) {
71006 $this->assertInstanceOf('\InvalidArgumentException', $e, '->find() throws an \InvalidArgumentException if command does not exist');
71007 $this->assertRegExp(sprintf('/Command "%s" is not defined./', $commandName), $e->getMessage(), '->find() throws an \InvalidArgumentException if command does not exist, with alternatives');
71008 $this->assertFalse(strpos($e->getMessage(), 'foo:bar'), '->find() throws an \InvalidArgumentException if command does not exist, without "foo:bar" alternative');
71009 }
71010 }
71011
71012 public function testFindAlternativeNamespace()
71013 {
71014 $application = new Application();
71015
71016 $application->add(new \FooCommand());
71017 $application->add(new \Foo1Command());
71018 $application->add(new \Foo2Command());
71019 $application->add(new \foo3Command());
71020
71021 try {
71022 $application->find('Unknown-namespace:Unknown-command');
71023 $this->fail('->find() throws an \InvalidArgumentException if namespace does not exist');
71024 } catch (\Exception $e) {
71025 $this->assertInstanceOf('\InvalidArgumentException', $e, '->find() throws an \InvalidArgumentException if namespace does not exist');
71026 $this->assertEquals('There are no commands defined in the "Unknown-namespace" namespace.', $e->getMessage(), '->find() throws an \InvalidArgumentException if namespace does not exist, without alternatives');
71027 }
71028
71029 try {
71030 $application->find('foo2:command');
71031 $this->fail('->find() throws an \InvalidArgumentException if namespace does not exist');
71032 } catch (\Exception $e) {
71033 $this->assertInstanceOf('\InvalidArgumentException', $e, '->find() throws an \InvalidArgumentException if namespace does not exist');
71034 $this->assertRegExp('/There are no commands defined in the "foo2" namespace./', $e->getMessage(), '->find() throws an \InvalidArgumentException if namespace does not exist, with alternative');
71035 $this->assertRegExp('/foo/', $e->getMessage(), '->find() throws an \InvalidArgumentException if namespace does not exist, with alternative : "foo"');
71036 $this->assertRegExp('/foo1/', $e->getMessage(), '->find() throws an \InvalidArgumentException if namespace does not exist, with alternative : "foo1"');
71037 $this->assertRegExp('/foo3/', $e->getMessage(), '->find() throws an \InvalidArgumentException if namespace does not exist, with alternative : "foo3"');
71038 }
71039 }
71040
71041 public function testFindNamespaceDoesNotFailOnDeepSimilarNamespaces()
71042 {
71043 $application = $this->getMock('Symfony\Component\Console\Application', array('getNamespaces'));
71044 $application->expects($this->once())
71045 ->method('getNamespaces')
71046 ->will($this->returnValue(array('foo:sublong', 'bar:sub')));
71047
71048 $this->assertEquals('foo:sublong', $application->findNamespace('f:sub'));
71049 }
71050
71051 public function testSetCatchExceptions()
71052 {
71053 $application = $this->getMock('Symfony\Component\Console\Application', array('getTerminalWidth'));
71054 $application->setAutoExit(false);
71055 $application->expects($this->any())
71056 ->method('getTerminalWidth')
71057 ->will($this->returnValue(120));
71058 $tester = new ApplicationTester($application);
71059
71060 $application->setCatchExceptions(true);
71061 $tester->run(array('command' => 'foo'), array('decorated' => false));
71062 $this->assertStringEqualsFile(self::$fixturesPath.'/application_renderexception1.txt', $tester->getDisplay(true), '->setCatchExceptions() sets the catch exception flag');
71063
71064 $application->setCatchExceptions(false);
71065 try {
71066 $tester->run(array('command' => 'foo'), array('decorated' => false));
71067 $this->fail('->setCatchExceptions() sets the catch exception flag');
71068 } catch (\Exception $e) {
71069 $this->assertInstanceOf('\Exception', $e, '->setCatchExceptions() sets the catch exception flag');
71070 $this->assertEquals('Command "foo" is not defined.', $e->getMessage(), '->setCatchExceptions() sets the catch exception flag');
71071 }
71072 }
71073
71074 public function testAsText()
71075 {
71076 $application = new Application();
71077 $application->add(new \FooCommand);
71078 $this->ensureStaticCommandHelp($application);
71079 $this->assertStringEqualsFile(self::$fixturesPath.'/application_astext1.txt', $this->normalizeLineBreaks($application->asText()), '->asText() returns a text representation of the application');
71080 $this->assertStringEqualsFile(self::$fixturesPath.'/application_astext2.txt', $this->normalizeLineBreaks($application->asText('foo')), '->asText() returns a text representation of the application');
71081 }
71082
71083 public function testAsXml()
71084 {
71085 $application = new Application();
71086 $application->add(new \FooCommand);
71087 $this->ensureStaticCommandHelp($application);
71088 $this->assertXmlStringEqualsXmlFile(self::$fixturesPath.'/application_asxml1.txt', $application->asXml(), '->asXml() returns an XML representation of the application');
71089 $this->assertXmlStringEqualsXmlFile(self::$fixturesPath.'/application_asxml2.txt', $application->asXml('foo'), '->asXml() returns an XML representation of the application');
71090 }
71091
71092 public function testRenderException()
71093 {
71094 $application = $this->getMock('Symfony\Component\Console\Application', array('getTerminalWidth'));
71095 $application->setAutoExit(false);
71096 $application->expects($this->any())
71097 ->method('getTerminalWidth')
71098 ->will($this->returnValue(120));
71099 $tester = new ApplicationTester($application);
71100
71101 $tester->run(array('command' => 'foo'), array('decorated' => false));
71102 $this->assertStringEqualsFile(self::$fixturesPath.'/application_renderexception1.txt', $tester->getDisplay(true), '->renderException() renders a pretty exception');
71103
71104 $tester->run(array('command' => 'foo'), array('decorated' => false, 'verbosity' => Output::VERBOSITY_VERBOSE));
71105 $this->assertContains('Exception trace', $tester->getDisplay(), '->renderException() renders a pretty exception with a stack trace when verbosity is verbose');
71106
71107 $tester->run(array('command' => 'list', '--foo' => true), array('decorated' => false));
71108 $this->assertStringEqualsFile(self::$fixturesPath.'/application_renderexception2.txt', $tester->getDisplay(true), '->renderException() renders the command synopsis when an exception occurs in the context of a command');
71109
71110 $application->add(new \Foo3Command);
71111 $tester = new ApplicationTester($application);
71112 $tester->run(array('command' => 'foo3:bar'), array('decorated' => false));
71113 $this->assertStringEqualsFile(self::$fixturesPath.'/application_renderexception3.txt', $tester->getDisplay(true), '->renderException() renders a pretty exceptions with previous exceptions');
71114
71115 $application = $this->getMock('Symfony\Component\Console\Application', array('getTerminalWidth'));
71116 $application->setAutoExit(false);
71117 $application->expects($this->any())
71118 ->method('getTerminalWidth')
71119 ->will($this->returnValue(32));
71120 $tester = new ApplicationTester($application);
71121
71122 $tester->run(array('command' => 'foo'), array('decorated' => false));
71123 $this->assertStringEqualsFile(self::$fixturesPath.'/application_renderexception4.txt', $tester->getDisplay(true), '->renderException() wraps messages when they are bigger than the terminal');
71124 }
71125
71126 public function testRun()
71127 {
71128 $application = new Application();
71129 $application->setAutoExit(false);
71130 $application->setCatchExceptions(false);
71131 $application->add($command = new \Foo1Command());
71132 $_SERVER['argv'] = array('cli.php', 'foo:bar1');
71133
71134 ob_start();
71135 $application->run();
71136 ob_end_clean();
71137
71138 $this->assertSame('Symfony\Component\Console\Input\ArgvInput', get_class($command->input), '->run() creates an ArgvInput by default if none is given');
71139 $this->assertSame('Symfony\Component\Console\Output\ConsoleOutput', get_class($command->output), '->run() creates a ConsoleOutput by default if none is given');
71140
71141 $application = new Application();
71142 $application->setAutoExit(false);
71143 $application->setCatchExceptions(false);
71144
71145 $this->ensureStaticCommandHelp($application);
71146 $tester = new ApplicationTester($application);
71147
71148 $tester->run(array(), array('decorated' => false));
71149 $this->assertStringEqualsFile(self::$fixturesPath.'/application_run1.txt', $tester->getDisplay(true), '->run() runs the list command if no argument is passed');
71150
71151 $tester->run(array('--help' => true), array('decorated' => false));
71152 $this->assertStringEqualsFile(self::$fixturesPath.'/application_run2.txt', $tester->getDisplay(true), '->run() runs the help command if --help is passed');
71153
71154 $tester->run(array('-h' => true), array('decorated' => false));
71155 $this->assertStringEqualsFile(self::$fixturesPath.'/application_run2.txt', $tester->getDisplay(true), '->run() runs the help command if -h is passed');
71156
71157 $tester->run(array('command' => 'list', '--help' => true), array('decorated' => false));
71158 $this->assertStringEqualsFile(self::$fixturesPath.'/application_run3.txt', $tester->getDisplay(true), '->run() displays the help if --help is passed');
71159
71160 $tester->run(array('command' => 'list', '-h' => true), array('decorated' => false));
71161 $this->assertStringEqualsFile(self::$fixturesPath.'/application_run3.txt', $tester->getDisplay(true), '->run() displays the help if -h is passed');
71162
71163 $tester->run(array('--ansi' => true));
71164 $this->assertTrue($tester->getOutput()->isDecorated(), '->run() forces color output if --ansi is passed');
71165
71166 $tester->run(array('--no-ansi' => true));
71167 $this->assertFalse($tester->getOutput()->isDecorated(), '->run() forces color output to be disabled if --no-ansi is passed');
71168
71169 $tester->run(array('--version' => true), array('decorated' => false));
71170 $this->assertStringEqualsFile(self::$fixturesPath.'/application_run4.txt', $tester->getDisplay(true), '->run() displays the program version if --version is passed');
71171
71172 $tester->run(array('-V' => true), array('decorated' => false));
71173 $this->assertStringEqualsFile(self::$fixturesPath.'/application_run4.txt', $tester->getDisplay(true), '->run() displays the program version if -v is passed');
71174
71175 $tester->run(array('command' => 'list', '--quiet' => true));
71176 $this->assertSame('', $tester->getDisplay(), '->run() removes all output if --quiet is passed');
71177
71178 $tester->run(array('command' => 'list', '-q' => true));
71179 $this->assertSame('', $tester->getDisplay(), '->run() removes all output if -q is passed');
71180
71181 $tester->run(array('command' => 'list', '--verbose' => true));
71182 $this->assertSame(Output::VERBOSITY_VERBOSE, $tester->getOutput()->getVerbosity(), '->run() sets the output to verbose if --verbose is passed');
71183
71184 $tester->run(array('command' => 'list', '--verbose' => 1));
71185 $this->assertSame(Output::VERBOSITY_VERBOSE, $tester->getOutput()->getVerbosity(), '->run() sets the output to verbose if --verbose=1 is passed');
71186
71187 $tester->run(array('command' => 'list', '--verbose' => 2));
71188 $this->assertSame(Output::VERBOSITY_VERY_VERBOSE, $tester->getOutput()->getVerbosity(), '->run() sets the output to very verbose if --verbose=2 is passed');
71189
71190 $tester->run(array('command' => 'list', '--verbose' => 3));
71191 $this->assertSame(Output::VERBOSITY_DEBUG, $tester->getOutput()->getVerbosity(), '->run() sets the output to debug if --verbose=3 is passed');
71192
71193 $tester->run(array('command' => 'list', '--verbose' => 4));
71194 $this->assertSame(Output::VERBOSITY_VERBOSE, $tester->getOutput()->getVerbosity(), '->run() sets the output to verbose if unknown --verbose level is passed');
71195
71196 $tester->run(array('command' => 'list', '-v' => true));
71197 $this->assertSame(Output::VERBOSITY_VERBOSE, $tester->getOutput()->getVerbosity(), '->run() sets the output to verbose if -v is passed');
71198
71199 $tester->run(array('command' => 'list', '-vv' => true));
71200 $this->assertSame(Output::VERBOSITY_VERY_VERBOSE, $tester->getOutput()->getVerbosity(), '->run() sets the output to verbose if -v is passed');
71201
71202 $tester->run(array('command' => 'list', '-vvv' => true));
71203 $this->assertSame(Output::VERBOSITY_DEBUG, $tester->getOutput()->getVerbosity(), '->run() sets the output to verbose if -v is passed');
71204
71205 $application = new Application();
71206 $application->setAutoExit(false);
71207 $application->setCatchExceptions(false);
71208 $application->add(new \FooCommand());
71209 $tester = new ApplicationTester($application);
71210
71211 $tester->run(array('command' => 'foo:bar', '--no-interaction' => true), array('decorated' => false));
71212 $this->assertSame('called'.PHP_EOL, $tester->getDisplay(), '->run() does not call interact() if --no-interaction is passed');
71213
71214 $tester->run(array('command' => 'foo:bar', '-n' => true), array('decorated' => false));
71215 $this->assertSame('called'.PHP_EOL, $tester->getDisplay(), '->run() does not call interact() if -n is passed');
71216 }
71217
71218 public function testRunReturnsIntegerExitCode()
71219 {
71220 $exception = new \Exception('', 4);
71221
71222 $application = $this->getMock('Symfony\Component\Console\Application', array('doRun'));
71223 $application->setAutoExit(false);
71224 $application->expects($this->once())
71225 ->method('doRun')
71226 ->will($this->throwException($exception));
71227
71228 $exitCode = $application->run(new ArrayInput(array()), new NullOutput());
71229
71230 $this->assertSame(4, $exitCode, '->run() returns integer exit code extracted from raised exception');
71231 }
71232
71233 public function testRunReturnsExitCodeOneForExceptionCodeZero()
71234 {
71235 $exception = new \Exception('', 0);
71236
71237 $application = $this->getMock('Symfony\Component\Console\Application', array('doRun'));
71238 $application->setAutoExit(false);
71239 $application->expects($this->once())
71240 ->method('doRun')
71241 ->will($this->throwException($exception));
71242
71243 $exitCode = $application->run(new ArrayInput(array()), new NullOutput());
71244
71245 $this->assertSame(1, $exitCode, '->run() returns exit code 1 when exception code is 0');
71246 }
71247
71248 /**
71249 * @expectedException \LogicException
71250 * @dataProvider getAddingAlreadySetDefinitionElementData
71251 */
71252 public function testAddingAlreadySetDefinitionElementData($def)
71253 {
71254 $application = new Application();
71255 $application->setAutoExit(false);
71256 $application->setCatchExceptions(false);
71257 $application
71258 ->register('foo')
71259 ->setDefinition(array($def))
71260 ->setCode(function (InputInterface $input, OutputInterface $output) {})
71261 ;
71262
71263 $input = new ArrayInput(array('command' => 'foo'));
71264 $output = new NullOutput();
71265 $application->run($input, $output);
71266 }
71267
71268 public function getAddingAlreadySetDefinitionElementData()
71269 {
71270 return array(
71271 array(new InputArgument('command', InputArgument::REQUIRED)),
71272 array(new InputOption('quiet', '', InputOption::VALUE_NONE)),
71273 array(new InputOption('query', 'q', InputOption::VALUE_NONE)),
71274 );
71275 }
71276
71277 public function testGetDefaultHelperSetReturnsDefaultValues()
71278 {
71279 $application = new Application();
71280 $application->setAutoExit(false);
71281 $application->setCatchExceptions(false);
71282
71283 $helperSet = $application->getHelperSet();
71284
71285 $this->assertTrue($helperSet->has('formatter'));
71286 $this->assertTrue($helperSet->has('dialog'));
71287 $this->assertTrue($helperSet->has('progress'));
71288 }
71289
71290 public function testAddingSingleHelperSetOverwritesDefaultValues()
71291 {
71292 $application = new Application();
71293 $application->setAutoExit(false);
71294 $application->setCatchExceptions(false);
71295
71296 $application->setHelperSet(new HelperSet(array(new FormatterHelper())));
71297
71298 $helperSet = $application->getHelperSet();
71299
71300 $this->assertTrue($helperSet->has('formatter'));
71301
71302 // no other default helper set should be returned
71303 $this->assertFalse($helperSet->has('dialog'));
71304 $this->assertFalse($helperSet->has('progress'));
71305 }
71306
71307 public function testOverwritingDefaultHelperSetOverwritesDefaultValues()
71308 {
71309 $application = new CustomApplication();
71310 $application->setAutoExit(false);
71311 $application->setCatchExceptions(false);
71312
71313 $application->setHelperSet(new HelperSet(array(new FormatterHelper())));
71314
71315 $helperSet = $application->getHelperSet();
71316
71317 $this->assertTrue($helperSet->has('formatter'));
71318
71319 // no other default helper set should be returned
71320 $this->assertFalse($helperSet->has('dialog'));
71321 $this->assertFalse($helperSet->has('progress'));
71322 }
71323
71324 public function testGetDefaultInputDefinitionReturnsDefaultValues()
71325 {
71326 $application = new Application();
71327 $application->setAutoExit(false);
71328 $application->setCatchExceptions(false);
71329
71330 $inputDefinition = $application->getDefinition();
71331
71332 $this->assertTrue($inputDefinition->hasArgument('command'));
71333
71334 $this->assertTrue($inputDefinition->hasOption('help'));
71335 $this->assertTrue($inputDefinition->hasOption('quiet'));
71336 $this->assertTrue($inputDefinition->hasOption('verbose'));
71337 $this->assertTrue($inputDefinition->hasOption('version'));
71338 $this->assertTrue($inputDefinition->hasOption('ansi'));
71339 $this->assertTrue($inputDefinition->hasOption('no-ansi'));
71340 $this->assertTrue($inputDefinition->hasOption('no-interaction'));
71341 }
71342
71343 public function testOverwritingDefaultInputDefinitionOverwritesDefaultValues()
71344 {
71345 $application = new CustomApplication();
71346 $application->setAutoExit(false);
71347 $application->setCatchExceptions(false);
71348
71349 $inputDefinition = $application->getDefinition();
71350
71351 // check whether the default arguments and options are not returned any more
71352 $this->assertFalse($inputDefinition->hasArgument('command'));
71353
71354 $this->assertFalse($inputDefinition->hasOption('help'));
71355 $this->assertFalse($inputDefinition->hasOption('quiet'));
71356 $this->assertFalse($inputDefinition->hasOption('verbose'));
71357 $this->assertFalse($inputDefinition->hasOption('version'));
71358 $this->assertFalse($inputDefinition->hasOption('ansi'));
71359 $this->assertFalse($inputDefinition->hasOption('no-ansi'));
71360 $this->assertFalse($inputDefinition->hasOption('no-interaction'));
71361
71362 $this->assertTrue($inputDefinition->hasOption('custom'));
71363 }
71364
71365 public function testSettingCustomInputDefinitionOverwritesDefaultValues()
71366 {
71367 $application = new Application();
71368 $application->setAutoExit(false);
71369 $application->setCatchExceptions(false);
71370
71371 $application->setDefinition(new InputDefinition(array(new InputOption('--custom', '-c', InputOption::VALUE_NONE, 'Set the custom input definition.'))));
71372
71373 $inputDefinition = $application->getDefinition();
71374
71375 // check whether the default arguments and options are not returned any more
71376 $this->assertFalse($inputDefinition->hasArgument('command'));
71377
71378 $this->assertFalse($inputDefinition->hasOption('help'));
71379 $this->assertFalse($inputDefinition->hasOption('quiet'));
71380 $this->assertFalse($inputDefinition->hasOption('verbose'));
71381 $this->assertFalse($inputDefinition->hasOption('version'));
71382 $this->assertFalse($inputDefinition->hasOption('ansi'));
71383 $this->assertFalse($inputDefinition->hasOption('no-ansi'));
71384 $this->assertFalse($inputDefinition->hasOption('no-interaction'));
71385
71386 $this->assertTrue($inputDefinition->hasOption('custom'));
71387 }
71388
71389 public function testRunWithDispatcher()
71390 {
71391 if (!class_exists('Symfony\Component\EventDispatcher\EventDispatcher')) {
71392 $this->markTestSkipped('The "EventDispatcher" component is not available');
71393 }
71394
71395 $application = new Application();
71396 $application->setAutoExit(false);
71397 $application->setDispatcher($this->getDispatcher());
71398
71399 $application->register('foo')->setCode(function (InputInterface $input, OutputInterface $output) {
71400 $output->write('foo.');
71401 });
71402
71403 $tester = new ApplicationTester($application);
71404 $tester->run(array('command' => 'foo'));
71405 $this->assertEquals('before.foo.after.', $tester->getDisplay());
71406 }
71407
71408 /**
71409 * @expectedException \LogicException
71410 * @expectedExceptionMessage caught
71411 */
71412 public function testRunWithExceptionAndDispatcher()
71413 {
71414 if (!class_exists('Symfony\Component\EventDispatcher\EventDispatcher')) {
71415 $this->markTestSkipped('The "EventDispatcher" component is not available');
71416 }
71417
71418 $application = new Application();
71419 $application->setDispatcher($this->getDispatcher());
71420 $application->setAutoExit(false);
71421 $application->setCatchExceptions(false);
71422
71423 $application->register('foo')->setCode(function (InputInterface $input, OutputInterface $output) {
71424 throw new \RuntimeException('foo');
71425 });
71426
71427 $tester = new ApplicationTester($application);
71428 $tester->run(array('command' => 'foo'));
71429 }
71430
71431 public function testRunDispatchesAllEventsWithException()
71432 {
71433 if (!class_exists('Symfony\Component\EventDispatcher\EventDispatcher')) {
71434 $this->markTestSkipped('The "EventDispatcher" component is not available');
71435 }
71436
71437 $application = new Application();
71438 $application->setDispatcher($this->getDispatcher());
71439 $application->setAutoExit(false);
71440
71441 $application->register('foo')->setCode(function (InputInterface $input, OutputInterface $output) {
71442 $output->write('foo.');
71443
71444 throw new \RuntimeException('foo');
71445 });
71446
71447 $tester = new ApplicationTester($application);
71448 $tester->run(array('command' => 'foo'));
71449 $this->assertContains('before.foo.after.caught.', $tester->getDisplay());
71450 }
71451
71452 protected function getDispatcher()
71453 {
71454 $dispatcher = new EventDispatcher;
71455 $dispatcher->addListener('console.command', function (ConsoleCommandEvent $event) {
71456 $event->getOutput()->write('before.');
71457 });
71458 $dispatcher->addListener('console.terminate', function (ConsoleTerminateEvent $event) {
71459 $event->getOutput()->write('after.');
71460
71461 $event->setExitCode(128);
71462 });
71463 $dispatcher->addListener('console.exception', function (ConsoleExceptionEvent $event) {
71464 $event->getOutput()->writeln('caught.');
71465
71466 $event->setException(new \LogicException('caught.', $event->getExitCode(), $event->getException()));
71467 });
71468
71469 return $dispatcher;
71470 }
71471 }
71472
71473 class CustomApplication extends Application
71474 {
71475 /**
71476 * Overwrites the default input definition.
71477 *
71478 * @return InputDefinition An InputDefinition instance
71479 */
71480 protected function getDefaultInputDefinition()
71481 {
71482 return new InputDefinition(array(new InputOption('--custom', '-c', InputOption::VALUE_NONE, 'Set the custom input definition.')));
71483 }
71484
71485 /**
71486 * Gets the default helper set with the helpers that should always be available.
71487 *
71488 * @return HelperSet A HelperSet instance
71489 */
71490 protected function getDefaultHelperSet()
71491 {
71492 return new HelperSet(array(new FormatterHelper()));
71493 }
71494 }
71495 <?php
71496
71497 /*
71498 * This file is part of the Symfony package.
71499 *
71500 * (c) Fabien Potencier <fabien@symfony.com>
71501 *
71502 * For the full copyright and license information, please view the LICENSE
71503 * file that was distributed with this source code.
71504 */
71505
71506 namespace Symfony\Component\Console\Tests\Descriptor;
71507
71508 use Symfony\Component\Console\Descriptor\MarkdownDescriptor;
71509
71510 class MarkdownDescriptorTest extends AbstractDescriptorTest
71511 {
71512 protected function getDescriptor()
71513 {
71514 return new MarkdownDescriptor();
71515 }
71516
71517 protected function getFormat()
71518 {
71519 return 'md';
71520 }
71521 }
71522 <?php
71523
71524 /*
71525 * This file is part of the Symfony package.
71526 *
71527 * (c) Fabien Potencier <fabien@symfony.com>
71528 *
71529 * For the full copyright and license information, please view the LICENSE
71530 * file that was distributed with this source code.
71531 */
71532
71533 namespace Symfony\Component\Console\Tests\Descriptor;
71534
71535 use Symfony\Component\Console\Descriptor\XmlDescriptor;
71536
71537 class XmlDescriptorTest extends AbstractDescriptorTest
71538 {
71539 protected function getDescriptor()
71540 {
71541 return new XmlDescriptor();
71542 }
71543
71544 protected function getFormat()
71545 {
71546 return 'xml';
71547 }
71548 }
71549 <?php
71550
71551 /*
71552 * This file is part of the Symfony package.
71553 *
71554 * (c) Fabien Potencier <fabien@symfony.com>
71555 *
71556 * For the full copyright and license information, please view the LICENSE
71557 * file that was distributed with this source code.
71558 */
71559
71560 namespace Symfony\Component\Console\Tests\Descriptor;
71561
71562 use Symfony\Component\Console\Descriptor\TextDescriptor;
71563
71564 class TextDescriptorTest extends AbstractDescriptorTest
71565 {
71566 protected function getDescriptor()
71567 {
71568 return new TextDescriptor();
71569 }
71570
71571 protected function getFormat()
71572 {
71573 return 'txt';
71574 }
71575 }
71576 <?php
71577
71578 /*
71579 * This file is part of the Symfony package.
71580 *
71581 * (c) Fabien Potencier <fabien@symfony.com>
71582 *
71583 * For the full copyright and license information, please view the LICENSE
71584 * file that was distributed with this source code.
71585 */
71586
71587 namespace Symfony\Component\Console\Tests\Descriptor;
71588
71589 use Symfony\Component\Console\Application;
71590 use Symfony\Component\Console\Command\Command;
71591 use Symfony\Component\Console\Input\InputArgument;
71592 use Symfony\Component\Console\Input\InputDefinition;
71593 use Symfony\Component\Console\Input\InputOption;
71594
71595 abstract class AbstractDescriptorTest extends \PHPUnit_Framework_TestCase
71596 {
71597 /** @dataProvider getDescribeInputArgumentTestData */
71598 public function testDescribeInputArgument(InputArgument $argument, $expectedDescription)
71599 {
71600 $this->assertEquals(trim($expectedDescription), trim($this->getDescriptor()->describe($argument)));
71601 }
71602
71603 /** @dataProvider getDescribeInputOptionTestData */
71604 public function testDescribeInputOption(InputOption $option, $expectedDescription)
71605 {
71606 $this->assertEquals(trim($expectedDescription), trim($this->getDescriptor()->describe($option)));
71607 }
71608
71609 /** @dataProvider getDescribeInputDefinitionTestData */
71610 public function testDescribeInputDefinition(InputDefinition $definition, $expectedDescription)
71611 {
71612 $this->assertEquals(trim($expectedDescription), trim($this->getDescriptor()->describe($definition)));
71613 }
71614
71615 /** @dataProvider getDescribeCommandTestData */
71616 public function testDescribeCommand(Command $command, $expectedDescription)
71617 {
71618 $this->assertEquals(trim($expectedDescription), trim($this->getDescriptor()->describe($command)));
71619 }
71620
71621 /** @dataProvider getDescribeApplicationTestData */
71622 public function testDescribeApplication(Application $application, $expectedDescription)
71623 {
71624 // Replaces the dynamic placeholders of the command help text with a static version.
71625 // The placeholder %command.full_name% includes the script path that is not predictable
71626 // and can not be tested against.
71627 foreach ($application->all() as $command) {
71628 $command->setHelp(str_replace('%command.full_name%', 'app/console %command.name%', $command->getHelp()));
71629 }
71630
71631 $this->assertEquals(trim($expectedDescription), trim(str_replace(PHP_EOL, "\n", $this->getDescriptor()->describe($application))));
71632 }
71633
71634 public function getDescribeInputArgumentTestData()
71635 {
71636 return $this->getDescriptionTestData(ObjectsProvider::getInputArguments());
71637 }
71638
71639 public function getDescribeInputOptionTestData()
71640 {
71641 return $this->getDescriptionTestData(ObjectsProvider::getInputOptions());
71642 }
71643
71644 public function getDescribeInputDefinitionTestData()
71645 {
71646 return $this->getDescriptionTestData(ObjectsProvider::getInputDefinitions());
71647 }
71648
71649 public function getDescribeCommandTestData()
71650 {
71651 return $this->getDescriptionTestData(ObjectsProvider::getCommands());
71652 }
71653
71654 public function getDescribeApplicationTestData()
71655 {
71656 return $this->getDescriptionTestData(ObjectsProvider::getApplications());
71657 }
71658
71659 abstract protected function getDescriptor();
71660 abstract protected function getFormat();
71661
71662 private function getDescriptionTestData(array $objects)
71663 {
71664 $data = array();
71665 foreach ($objects as $name => $object) {
71666 $description = file_get_contents(sprintf('%s/../Fixtures/%s.%s', __DIR__, $name, $this->getFormat()));
71667 $data[] = array($object, $description);
71668 }
71669
71670 return $data;
71671 }
71672 }
71673 <?php
71674
71675 /*
71676 * This file is part of the Symfony package.
71677 *
71678 * (c) Fabien Potencier <fabien@symfony.com>
71679 *
71680 * For the full copyright and license information, please view the LICENSE
71681 * file that was distributed with this source code.
71682 */
71683
71684 namespace Symfony\Component\Console\Tests\Descriptor;
71685
71686 use Symfony\Component\Console\Descriptor\JsonDescriptor;
71687
71688 class JsonDescriptorTest extends AbstractDescriptorTest
71689 {
71690 protected function getDescriptor()
71691 {
71692 return new JsonDescriptor();
71693 }
71694
71695 protected function getFormat()
71696 {
71697 return 'json';
71698 }
71699 }
71700 <?php
71701
71702 /*
71703 * This file is part of the Symfony package.
71704 *
71705 * (c) Fabien Potencier <fabien@symfony.com>
71706 *
71707 * For the full copyright and license information, please view the LICENSE
71708 * file that was distributed with this source code.
71709 */
71710
71711 namespace Symfony\Component\Console\Tests\Descriptor;
71712
71713 use Symfony\Component\Console\Input\InputArgument;
71714 use Symfony\Component\Console\Input\InputDefinition;
71715 use Symfony\Component\Console\Input\InputOption;
71716 use Symfony\Component\Console\Tests\Fixtures\DescriptorApplication1;
71717 use Symfony\Component\Console\Tests\Fixtures\DescriptorApplication2;
71718 use Symfony\Component\Console\Tests\Fixtures\DescriptorCommand1;
71719 use Symfony\Component\Console\Tests\Fixtures\DescriptorCommand2;
71720
71721 /**
71722 * @author Jean-François Simon <contact@jfsimon.fr>
71723 */
71724 class ObjectsProvider
71725 {
71726 public static function getInputArguments()
71727 {
71728 return array(
71729 'input_argument_1' => new InputArgument('argument_name', InputArgument::REQUIRED),
71730 'input_argument_2' => new InputArgument('argument_name', InputArgument::IS_ARRAY, 'argument description'),
71731 'input_argument_3' => new InputArgument('argument_name', InputArgument::OPTIONAL, 'argument description', 'default_value'),
71732 );
71733 }
71734
71735 public static function getInputOptions()
71736 {
71737 return array(
71738 'input_option_1' => new InputOption('option_name', 'o', InputOption::VALUE_NONE),
71739 'input_option_2' => new InputOption('option_name', 'o', InputOption::VALUE_OPTIONAL, 'option description', 'default_value'),
71740 'input_option_3' => new InputOption('option_name', 'o', InputOption::VALUE_REQUIRED, 'option description'),
71741 'input_option_4' => new InputOption('option_name', 'o', InputOption::VALUE_IS_ARRAY | InputOption::VALUE_OPTIONAL, 'option description', array()),
71742 );
71743 }
71744
71745 public static function getInputDefinitions()
71746 {
71747 return array(
71748 'input_definition_1' => new InputDefinition(),
71749 'input_definition_2' => new InputDefinition(array(new InputArgument('argument_name', InputArgument::REQUIRED))),
71750 'input_definition_3' => new InputDefinition(array(new InputOption('option_name', 'o', InputOption::VALUE_NONE))),
71751 'input_definition_4' => new InputDefinition(array(
71752 new InputArgument('argument_name', InputArgument::REQUIRED),
71753 new InputOption('option_name', 'o', InputOption::VALUE_NONE),
71754 )),
71755 );
71756 }
71757
71758 public static function getCommands()
71759 {
71760 return array(
71761 'command_1' => new DescriptorCommand1(),
71762 'command_2' => new DescriptorCommand2(),
71763 );
71764 }
71765
71766 public static function getApplications()
71767 {
71768 return array(
71769 'application_1' => new DescriptorApplication1(),
71770 'application_2' => new DescriptorApplication2(),
71771 );
71772 }
71773 }
71774 <?php
71775
71776 /*
71777 * This file is part of the Symfony package.
71778 *
71779 * (c) Fabien Potencier <fabien@symfony.com>
71780 *
71781 * For the full copyright and license information, please view the LICENSE
71782 * file that was distributed with this source code.
71783 */
71784
71785 namespace Symfony\Component\Console\Tests\Command;
71786
71787 use Symfony\Component\Console\Tester\CommandTester;
71788 use Symfony\Component\Console\Application;
71789
71790 class ListCommandTest extends \PHPUnit_Framework_TestCase
71791 {
71792 public function testExecuteListsCommands()
71793 {
71794 $application = new Application();
71795 $commandTester = new CommandTester($command = $application->get('list'));
71796 $commandTester->execute(array('command' => $command->getName()), array('decorated' => false));
71797
71798 $this->assertRegExp('/help Displays help for a command/', $commandTester->getDisplay(), '->execute() returns a list of available commands');
71799 }
71800
71801 public function testExecuteListsCommandsWithXmlOption()
71802 {
71803 $application = new Application();
71804 $commandTester = new CommandTester($command = $application->get('list'));
71805 $commandTester->execute(array('command' => $command->getName(), '--format' => 'xml'));
71806 $this->assertRegExp('/<command id="list" name="list">/', $commandTester->getDisplay(), '->execute() returns a list of available commands in XML if --xml is passed');
71807 }
71808
71809 public function testExecuteListsCommandsWithRawOption()
71810 {
71811 $application = new Application();
71812 $commandTester = new CommandTester($command = $application->get('list'));
71813 $commandTester->execute(array('command' => $command->getName(), '--raw' => true));
71814 $output = <<<EOF
71815 help Displays help for a command
71816 list Lists commands
71817
71818 EOF;
71819
71820 $this->assertEquals($output, $commandTester->getDisplay(true));
71821 }
71822
71823 public function testExecuteListsCommandsWithNamespaceArgument()
71824 {
71825
71826 require_once(realpath(__DIR__.'/../Fixtures/FooCommand.php'));
71827 $application = new Application();
71828 $application->add(new \FooCommand());
71829 $commandTester = new CommandTester($command = $application->get('list'));
71830 $commandTester->execute(array('command' => $command->getName(), 'namespace' => 'foo', '--raw' => true));
71831 $output = <<<EOF
71832 foo:bar The foo:bar command
71833
71834 EOF;
71835
71836 $this->assertEquals($output, $commandTester->getDisplay(true));
71837 }
71838 }
71839 <?php
71840
71841 /*
71842 * This file is part of the Symfony package.
71843 *
71844 * (c) Fabien Potencier <fabien@symfony.com>
71845 *
71846 * For the full copyright and license information, please view the LICENSE
71847 * file that was distributed with this source code.
71848 */
71849
71850 namespace Symfony\Component\Console\Tests\Command;
71851
71852 use Symfony\Component\Console\Command\Command;
71853 use Symfony\Component\Console\Helper\FormatterHelper;
71854 use Symfony\Component\Console\Application;
71855 use Symfony\Component\Console\Input\InputDefinition;
71856 use Symfony\Component\Console\Input\InputArgument;
71857 use Symfony\Component\Console\Input\InputOption;
71858 use Symfony\Component\Console\Input\InputInterface;
71859 use Symfony\Component\Console\Input\StringInput;
71860 use Symfony\Component\Console\Output\OutputInterface;
71861 use Symfony\Component\Console\Output\NullOutput;
71862 use Symfony\Component\Console\Tester\CommandTester;
71863
71864 class CommandTest extends \PHPUnit_Framework_TestCase
71865 {
71866 protected static $fixturesPath;
71867
71868 public static function setUpBeforeClass()
71869 {
71870 self::$fixturesPath = __DIR__.'/../Fixtures/';
71871 require_once self::$fixturesPath.'/TestCommand.php';
71872 }
71873
71874 public function testConstructor()
71875 {
71876 $command = new Command('foo:bar');
71877 $this->assertEquals('foo:bar', $command->getName(), '__construct() takes the command name as its first argument');
71878 }
71879
71880 /**
71881 * @expectedException \LogicException
71882 * @expectedExceptionMessage The command name cannot be empty.
71883 */
71884 public function testCommandNameCannotBeEmpty()
71885 {
71886 new Command();
71887 }
71888
71889 public function testSetApplication()
71890 {
71891 $application = new Application();
71892 $command = new \TestCommand();
71893 $command->setApplication($application);
71894 $this->assertEquals($application, $command->getApplication(), '->setApplication() sets the current application');
71895 }
71896
71897 public function testSetGetDefinition()
71898 {
71899 $command = new \TestCommand();
71900 $ret = $command->setDefinition($definition = new InputDefinition());
71901 $this->assertEquals($command, $ret, '->setDefinition() implements a fluent interface');
71902 $this->assertEquals($definition, $command->getDefinition(), '->setDefinition() sets the current InputDefinition instance');
71903 $command->setDefinition(array(new InputArgument('foo'), new InputOption('bar')));
71904 $this->assertTrue($command->getDefinition()->hasArgument('foo'), '->setDefinition() also takes an array of InputArguments and InputOptions as an argument');
71905 $this->assertTrue($command->getDefinition()->hasOption('bar'), '->setDefinition() also takes an array of InputArguments and InputOptions as an argument');
71906 $command->setDefinition(new InputDefinition());
71907 }
71908
71909 public function testAddArgument()
71910 {
71911 $command = new \TestCommand();
71912 $ret = $command->addArgument('foo');
71913 $this->assertEquals($command, $ret, '->addArgument() implements a fluent interface');
71914 $this->assertTrue($command->getDefinition()->hasArgument('foo'), '->addArgument() adds an argument to the command');
71915 }
71916
71917 public function testAddOption()
71918 {
71919 $command = new \TestCommand();
71920 $ret = $command->addOption('foo');
71921 $this->assertEquals($command, $ret, '->addOption() implements a fluent interface');
71922 $this->assertTrue($command->getDefinition()->hasOption('foo'), '->addOption() adds an option to the command');
71923 }
71924
71925 public function testGetNamespaceGetNameSetName()
71926 {
71927 $command = new \TestCommand();
71928 $this->assertEquals('namespace:name', $command->getName(), '->getName() returns the command name');
71929 $command->setName('foo');
71930 $this->assertEquals('foo', $command->getName(), '->setName() sets the command name');
71931
71932 $ret = $command->setName('foobar:bar');
71933 $this->assertEquals($command, $ret, '->setName() implements a fluent interface');
71934 $this->assertEquals('foobar:bar', $command->getName(), '->setName() sets the command name');
71935 }
71936
71937 /**
71938 * @dataProvider provideInvalidCommandNames
71939 */
71940 public function testInvalidCommandNames($name)
71941 {
71942 $this->setExpectedException('InvalidArgumentException', sprintf('Command name "%s" is invalid.', $name));
71943
71944 $command = new \TestCommand();
71945 $command->setName($name);
71946 }
71947
71948 public function provideInvalidCommandNames()
71949 {
71950 return array(
71951 array(''),
71952 array('foo:')
71953 );
71954 }
71955
71956 public function testGetSetDescription()
71957 {
71958 $command = new \TestCommand();
71959 $this->assertEquals('description', $command->getDescription(), '->getDescription() returns the description');
71960 $ret = $command->setDescription('description1');
71961 $this->assertEquals($command, $ret, '->setDescription() implements a fluent interface');
71962 $this->assertEquals('description1', $command->getDescription(), '->setDescription() sets the description');
71963 }
71964
71965 public function testGetSetHelp()
71966 {
71967 $command = new \TestCommand();
71968 $this->assertEquals('help', $command->getHelp(), '->getHelp() returns the help');
71969 $ret = $command->setHelp('help1');
71970 $this->assertEquals($command, $ret, '->setHelp() implements a fluent interface');
71971 $this->assertEquals('help1', $command->getHelp(), '->setHelp() sets the help');
71972 }
71973
71974 public function testGetProcessedHelp()
71975 {
71976 $command = new \TestCommand();
71977 $command->setHelp('The %command.name% command does... Example: php %command.full_name%.');
71978 $this->assertContains('The namespace:name command does...', $command->getProcessedHelp(), '->getProcessedHelp() replaces %command.name% correctly');
71979 $this->assertNotContains('%command.full_name%', $command->getProcessedHelp(), '->getProcessedHelp() replaces %command.full_name%');
71980 }
71981
71982 public function testGetSetAliases()
71983 {
71984 $command = new \TestCommand();
71985 $this->assertEquals(array('name'), $command->getAliases(), '->getAliases() returns the aliases');
71986 $ret = $command->setAliases(array('name1'));
71987 $this->assertEquals($command, $ret, '->setAliases() implements a fluent interface');
71988 $this->assertEquals(array('name1'), $command->getAliases(), '->setAliases() sets the aliases');
71989 }
71990
71991 public function testGetSynopsis()
71992 {
71993 $command = new \TestCommand();
71994 $command->addOption('foo');
71995 $command->addArgument('foo');
71996 $this->assertEquals('namespace:name [--foo] [foo]', $command->getSynopsis(), '->getSynopsis() returns the synopsis');
71997 }
71998
71999 public function testGetHelper()
72000 {
72001 $application = new Application();
72002 $command = new \TestCommand();
72003 $command->setApplication($application);
72004 $formatterHelper = new FormatterHelper();
72005 $this->assertEquals($formatterHelper->getName(), $command->getHelper('formatter')->getName(), '->getHelper() returns the correct helper');
72006 }
72007
72008 public function testGet()
72009 {
72010 $application = new Application();
72011 $command = new \TestCommand();
72012 $command->setApplication($application);
72013 $formatterHelper = new FormatterHelper();
72014 $this->assertEquals($formatterHelper->getName(), $command->getHelper('formatter')->getName(), '->__get() returns the correct helper');
72015 }
72016
72017 public function testMergeApplicationDefinition()
72018 {
72019 $application1 = new Application();
72020 $application1->getDefinition()->addArguments(array(new InputArgument('foo')));
72021 $application1->getDefinition()->addOptions(array(new InputOption('bar')));
72022 $command = new \TestCommand();
72023 $command->setApplication($application1);
72024 $command->setDefinition($definition = new InputDefinition(array(new InputArgument('bar'), new InputOption('foo'))));
72025
72026 $r = new \ReflectionObject($command);
72027 $m = $r->getMethod('mergeApplicationDefinition');
72028 $m->setAccessible(true);
72029 $m->invoke($command);
72030 $this->assertTrue($command->getDefinition()->hasArgument('foo'), '->mergeApplicationDefinition() merges the application arguments and the command arguments');
72031 $this->assertTrue($command->getDefinition()->hasArgument('bar'), '->mergeApplicationDefinition() merges the application arguments and the command arguments');
72032 $this->assertTrue($command->getDefinition()->hasOption('foo'), '->mergeApplicationDefinition() merges the application options and the command options');
72033 $this->assertTrue($command->getDefinition()->hasOption('bar'), '->mergeApplicationDefinition() merges the application options and the command options');
72034
72035 $m->invoke($command);
72036 $this->assertEquals(3, $command->getDefinition()->getArgumentCount(), '->mergeApplicationDefinition() does not try to merge twice the application arguments and options');
72037 }
72038
72039 public function testMergeApplicationDefinitionWithoutArgsThenWithArgsAddsArgs()
72040 {
72041 $application1 = new Application();
72042 $application1->getDefinition()->addArguments(array(new InputArgument('foo')));
72043 $application1->getDefinition()->addOptions(array(new InputOption('bar')));
72044 $command = new \TestCommand();
72045 $command->setApplication($application1);
72046 $command->setDefinition($definition = new InputDefinition(array()));
72047
72048 $r = new \ReflectionObject($command);
72049 $m = $r->getMethod('mergeApplicationDefinition');
72050 $m->setAccessible(true);
72051 $m->invoke($command, false);
72052 $this->assertTrue($command->getDefinition()->hasOption('bar'), '->mergeApplicationDefinition(false) merges the application and the commmand options');
72053 $this->assertFalse($command->getDefinition()->hasArgument('foo'), '->mergeApplicationDefinition(false) does not merge the application arguments');
72054
72055 $m->invoke($command, true);
72056 $this->assertTrue($command->getDefinition()->hasArgument('foo'), '->mergeApplicationDefinition(true) merges the application arguments and the command arguments');
72057
72058 $m->invoke($command);
72059 $this->assertEquals(2, $command->getDefinition()->getArgumentCount(), '->mergeApplicationDefinition() does not try to merge twice the application arguments');
72060 }
72061
72062 public function testRunInteractive()
72063 {
72064 $tester = new CommandTester(new \TestCommand());
72065
72066 $tester->execute(array(), array('interactive' => true));
72067
72068 $this->assertEquals('interact called'.PHP_EOL.'execute called'.PHP_EOL, $tester->getDisplay(), '->run() calls the interact() method if the input is interactive');
72069 }
72070
72071 public function testRunNonInteractive()
72072 {
72073 $tester = new CommandTester(new \TestCommand());
72074
72075 $tester->execute(array(), array('interactive' => false));
72076
72077 $this->assertEquals('execute called'.PHP_EOL, $tester->getDisplay(), '->run() does not call the interact() method if the input is not interactive');
72078 }
72079
72080 /**
72081 * @expectedException \LogicException
72082 * @expectedExceptionMessage You must override the execute() method in the concrete command class.
72083 */
72084 public function testExecuteMethodNeedsToBeOverriden()
72085 {
72086 $command = new Command('foo');
72087 $command->run(new StringInput(''), new NullOutput());
72088 }
72089
72090 /**
72091 * @expectedException \InvalidArgumentException
72092 * @expectedExceptionMessage The "--bar" option does not exist.
72093 */
72094 public function testRunWithInvalidOption()
72095 {
72096 $command = new \TestCommand();
72097 $tester = new CommandTester($command);
72098 $tester->execute(array('--bar' => true));
72099 }
72100
72101 public function testRunReturnsIntegerExitCode()
72102 {
72103 $command = new \TestCommand();
72104 $exitCode = $command->run(new StringInput(''), new NullOutput());
72105 $this->assertSame(0, $exitCode, '->run() returns integer exit code (treats null as 0)');
72106
72107 $command = $this->getMock('TestCommand', array('execute'));
72108 $command->expects($this->once())
72109 ->method('execute')
72110 ->will($this->returnValue('2.3'));
72111 $exitCode = $command->run(new StringInput(''), new NullOutput());
72112 $this->assertSame(2, $exitCode, '->run() returns integer exit code (casts numeric to int)');
72113 }
72114
72115 public function testRunReturnsAlwaysInteger()
72116 {
72117 $command = new \TestCommand();
72118
72119 $this->assertSame(0, $command->run(new StringInput(''), new NullOutput()));
72120 }
72121
72122 public function testSetCode()
72123 {
72124 $command = new \TestCommand();
72125 $ret = $command->setCode(function (InputInterface $input, OutputInterface $output) {
72126 $output->writeln('from the code...');
72127 });
72128 $this->assertEquals($command, $ret, '->setCode() implements a fluent interface');
72129 $tester = new CommandTester($command);
72130 $tester->execute(array());
72131 $this->assertEquals('interact called'.PHP_EOL.'from the code...'.PHP_EOL, $tester->getDisplay());
72132 }
72133
72134 public function testSetCodeWithNonClosureCallable()
72135 {
72136 $command = new \TestCommand();
72137 $ret = $command->setCode(array($this, 'callableMethodCommand'));
72138 $this->assertEquals($command, $ret, '->setCode() implements a fluent interface');
72139 $tester = new CommandTester($command);
72140 $tester->execute(array());
72141 $this->assertEquals('interact called'.PHP_EOL.'from the code...'.PHP_EOL, $tester->getDisplay());
72142 }
72143
72144 /**
72145 * @expectedException \InvalidArgumentException
72146 * @expectedExceptionMessage Invalid callable provided to Command::setCode.
72147 */
72148 public function testSetCodeWithNonCallable()
72149 {
72150 $command = new \TestCommand();
72151 $command->setCode(array($this, 'nonExistentMethod'));
72152 }
72153
72154 public function callableMethodCommand(InputInterface $input, OutputInterface $output)
72155 {
72156 $output->writeln('from the code...');
72157 }
72158
72159 public function testAsText()
72160 {
72161 $command = new \TestCommand();
72162 $command->setApplication(new Application());
72163 $tester = new CommandTester($command);
72164 $tester->execute(array('command' => $command->getName()));
72165 $this->assertStringEqualsFile(self::$fixturesPath.'/command_astext.txt', $command->asText(), '->asText() returns a text representation of the command');
72166 }
72167
72168 public function testAsXml()
72169 {
72170 $command = new \TestCommand();
72171 $command->setApplication(new Application());
72172 $tester = new CommandTester($command);
72173 $tester->execute(array('command' => $command->getName()));
72174 $this->assertXmlStringEqualsXmlFile(self::$fixturesPath.'/command_asxml.txt', $command->asXml(), '->asXml() returns an XML representation of the command');
72175 }
72176 }
72177 <?php
72178
72179 /*
72180 * This file is part of the Symfony package.
72181 *
72182 * (c) Fabien Potencier <fabien@symfony.com>
72183 *
72184 * For the full copyright and license information, please view the LICENSE
72185 * file that was distributed with this source code.
72186 */
72187
72188 namespace Symfony\Component\Console\Tests\Command;
72189
72190 use Symfony\Component\Console\Tester\CommandTester;
72191 use Symfony\Component\Console\Command\HelpCommand;
72192 use Symfony\Component\Console\Command\ListCommand;
72193 use Symfony\Component\Console\Application;
72194
72195 class HelpCommandTest extends \PHPUnit_Framework_TestCase
72196 {
72197 public function testExecuteForCommandAlias()
72198 {
72199 $command = new HelpCommand();
72200 $command->setApplication(new Application());
72201 $commandTester = new CommandTester($command);
72202 $commandTester->execute(array('command_name' => 'li'));
72203 $this->assertRegExp('/list \[--xml\] \[--raw\] \[--format="\.\.\."\] \[namespace\]/', $commandTester->getDisplay(), '->execute() returns a text help for the given command alias');
72204 }
72205
72206 public function testExecuteForCommand()
72207 {
72208 $command = new HelpCommand();
72209 $commandTester = new CommandTester($command);
72210 $command->setCommand(new ListCommand());
72211 $commandTester->execute(array());
72212 $this->assertRegExp('/list \[--xml\] \[--raw\] \[--format="\.\.\."\] \[namespace\]/', $commandTester->getDisplay(), '->execute() returns a text help for the given command');
72213 }
72214
72215 public function testExecuteForCommandWithXmlOption()
72216 {
72217 $command = new HelpCommand();
72218 $commandTester = new CommandTester($command);
72219 $command->setCommand(new ListCommand());
72220 $commandTester->execute(array('--format' => 'xml'));
72221 $this->assertRegExp('/<command/', $commandTester->getDisplay(), '->execute() returns an XML help text if --xml is passed');
72222 }
72223
72224 public function testExecuteForApplicationCommand()
72225 {
72226 $application = new Application();
72227 $commandTester = new CommandTester($application->get('help'));
72228 $commandTester->execute(array('command_name' => 'list'));
72229 $this->assertRegExp('/list \[--xml\] \[--raw\] \[--format="\.\.\."\] \[namespace\]/', $commandTester->getDisplay(), '->execute() returns a text help for the given command');
72230 }
72231
72232 public function testExecuteForApplicationCommandWithXmlOption()
72233 {
72234 $application = new Application();
72235 $commandTester = new CommandTester($application->get('help'));
72236 $commandTester->execute(array('command_name' => 'list', '--format' => 'xml'));
72237 $this->assertRegExp('/list \[--xml\] \[--raw\] \[--format="\.\.\."\] \[namespace\]/', $commandTester->getDisplay(), '->execute() returns a text help for the given command');
72238 $this->assertRegExp('/<command/', $commandTester->getDisplay(), '->execute() returns an XML help text if --format=xml is passed');
72239 }
72240 }
72241 <?php
72242
72243 /*
72244 * This file is part of the Symfony package.
72245 *
72246 * (c) Fabien Potencier <fabien@symfony.com>
72247 *
72248 * For the full copyright and license information, please view the LICENSE
72249 * file that was distributed with this source code.
72250 */
72251
72252 namespace Symfony\Component\Console\Tests\Tester;
72253
72254 use Symfony\Component\Console\Command\Command;
72255 use Symfony\Component\Console\Output\Output;
72256 use Symfony\Component\Console\Tester\CommandTester;
72257
72258 class CommandTesterTest extends \PHPUnit_Framework_TestCase
72259 {
72260 protected $command;
72261 protected $tester;
72262
72263 protected function setUp()
72264 {
72265 $this->command = new Command('foo');
72266 $this->command->addArgument('command');
72267 $this->command->addArgument('foo');
72268 $this->command->setCode(function ($input, $output) { $output->writeln('foo'); });
72269
72270 $this->tester = new CommandTester($this->command);
72271 $this->tester->execute(array('foo' => 'bar'), array('interactive' => false, 'decorated' => false, 'verbosity' => Output::VERBOSITY_VERBOSE));
72272 }
72273
72274 protected function tearDown()
72275 {
72276 $this->command = null;
72277 $this->tester = null;
72278 }
72279
72280 public function testExecute()
72281 {
72282 $this->assertFalse($this->tester->getInput()->isInteractive(), '->execute() takes an interactive option');
72283 $this->assertFalse($this->tester->getOutput()->isDecorated(), '->execute() takes a decorated option');
72284 $this->assertEquals(Output::VERBOSITY_VERBOSE, $this->tester->getOutput()->getVerbosity(), '->execute() takes a verbosity option');
72285 }
72286
72287 public function testGetInput()
72288 {
72289 $this->assertEquals('bar', $this->tester->getInput()->getArgument('foo'), '->getInput() returns the current input instance');
72290 }
72291
72292 public function testGetOutput()
72293 {
72294 rewind($this->tester->getOutput()->getStream());
72295 $this->assertEquals('foo'.PHP_EOL, stream_get_contents($this->tester->getOutput()->getStream()), '->getOutput() returns the current output instance');
72296 }
72297
72298 public function testGetDisplay()
72299 {
72300 $this->assertEquals('foo'.PHP_EOL, $this->tester->getDisplay(), '->getDisplay() returns the display of the last execution');
72301 }
72302 }
72303 <?php
72304
72305 /*
72306 * This file is part of the Symfony package.
72307 *
72308 * (c) Fabien Potencier <fabien@symfony.com>
72309 *
72310 * For the full copyright and license information, please view the LICENSE
72311 * file that was distributed with this source code.
72312 */
72313
72314 namespace Symfony\Component\Console\Tests\Tester;
72315
72316 use Symfony\Component\Console\Application;
72317 use Symfony\Component\Console\Output\Output;
72318 use Symfony\Component\Console\Tester\ApplicationTester;
72319
72320 class ApplicationTesterTest extends \PHPUnit_Framework_TestCase
72321 {
72322 protected $application;
72323 protected $tester;
72324
72325 protected function setUp()
72326 {
72327 $this->application = new Application();
72328 $this->application->setAutoExit(false);
72329 $this->application->register('foo')
72330 ->addArgument('foo')
72331 ->setCode(function ($input, $output) { $output->writeln('foo'); })
72332 ;
72333
72334 $this->tester = new ApplicationTester($this->application);
72335 $this->tester->run(array('command' => 'foo', 'foo' => 'bar'), array('interactive' => false, 'decorated' => false, 'verbosity' => Output::VERBOSITY_VERBOSE));
72336 }
72337
72338 protected function tearDown()
72339 {
72340 $this->application = null;
72341 $this->tester = null;
72342 }
72343
72344 public function testRun()
72345 {
72346 $this->assertFalse($this->tester->getInput()->isInteractive(), '->execute() takes an interactive option');
72347 $this->assertFalse($this->tester->getOutput()->isDecorated(), '->execute() takes a decorated option');
72348 $this->assertEquals(Output::VERBOSITY_VERBOSE, $this->tester->getOutput()->getVerbosity(), '->execute() takes a verbosity option');
72349 }
72350
72351 public function testGetInput()
72352 {
72353 $this->assertEquals('bar', $this->tester->getInput()->getArgument('foo'), '->getInput() returns the current input instance');
72354 }
72355
72356 public function testGetOutput()
72357 {
72358 rewind($this->tester->getOutput()->getStream());
72359 $this->assertEquals('foo'.PHP_EOL, stream_get_contents($this->tester->getOutput()->getStream()), '->getOutput() returns the current output instance');
72360 }
72361
72362 public function testGetDisplay()
72363 {
72364 $this->assertEquals('foo'.PHP_EOL, $this->tester->getDisplay(), '->getDisplay() returns the display of the last execution');
72365 }
72366 }
72367 <?php
72368
72369 /*
72370 * This file is part of the Symfony package.
72371 *
72372 * (c) Fabien Potencier <fabien@symfony.com>
72373 *
72374 * For the full copyright and license information, please view the LICENSE
72375 * file that was distributed with this source code.
72376 */
72377
72378 namespace Symfony\Component\Console\Tests\Input;
72379
72380 use Symfony\Component\Console\Input\ArgvInput;
72381 use Symfony\Component\Console\Input\InputDefinition;
72382 use Symfony\Component\Console\Input\InputArgument;
72383 use Symfony\Component\Console\Input\InputOption;
72384
72385 class ArgvInputTest extends \PHPUnit_Framework_TestCase
72386 {
72387 public function testConstructor()
72388 {
72389 $_SERVER['argv'] = array('cli.php', 'foo');
72390 $input = new ArgvInput();
72391 $r = new \ReflectionObject($input);
72392 $p = $r->getProperty('tokens');
72393 $p->setAccessible(true);
72394
72395 $this->assertEquals(array('foo'), $p->getValue($input), '__construct() automatically get its input from the argv server variable');
72396 }
72397
72398 public function testParseArguments()
72399 {
72400 $input = new ArgvInput(array('cli.php', 'foo'));
72401 $input->bind(new InputDefinition(array(new InputArgument('name'))));
72402 $this->assertEquals(array('name' => 'foo'), $input->getArguments(), '->parse() parses required arguments');
72403
72404 $input->bind(new InputDefinition(array(new InputArgument('name'))));
72405 $this->assertEquals(array('name' => 'foo'), $input->getArguments(), '->parse() is stateless');
72406 }
72407
72408 /**
72409 * @dataProvider provideOptions
72410 */
72411 public function testParseOptions($input, $options, $expectedOptions, $message)
72412 {
72413 $input = new ArgvInput($input);
72414 $input->bind(new InputDefinition($options));
72415
72416 $this->assertEquals($expectedOptions, $input->getOptions(), $message);
72417 }
72418
72419 public function provideOptions()
72420 {
72421 return array(
72422 array(
72423 array('cli.php', '--foo'),
72424 array(new InputOption('foo')),
72425 array('foo' => true),
72426 '->parse() parses long options without a value'
72427 ),
72428 array(
72429 array('cli.php', '--foo=bar'),
72430 array(new InputOption('foo', 'f', InputOption::VALUE_REQUIRED)),
72431 array('foo' => 'bar'),
72432 '->parse() parses long options with a required value (with a = separator)'
72433 ),
72434 array(
72435 array('cli.php', '--foo', 'bar'),
72436 array(new InputOption('foo', 'f', InputOption::VALUE_REQUIRED)),
72437 array('foo' => 'bar'),
72438 '->parse() parses long options with a required value (with a space separator)'
72439 ),
72440 array(
72441 array('cli.php', '-f'),
72442 array(new InputOption('foo', 'f')),
72443 array('foo' => true),
72444 '->parse() parses short options without a value'
72445 ),
72446 array(
72447 array('cli.php', '-fbar'),
72448 array(new InputOption('foo', 'f', InputOption::VALUE_REQUIRED)),
72449 array('foo' => 'bar'),
72450 '->parse() parses short options with a required value (with no separator)'
72451 ),
72452 array(
72453 array('cli.php', '-f', 'bar'),
72454 array(new InputOption('foo', 'f', InputOption::VALUE_REQUIRED)),
72455 array('foo' => 'bar'),
72456 '->parse() parses short options with a required value (with a space separator)'
72457 ),
72458 array(
72459 array('cli.php', '-f', ''),
72460 array(new InputOption('foo', 'f', InputOption::VALUE_OPTIONAL)),
72461 array('foo' => ''),
72462 '->parse() parses short options with an optional empty value'
72463 ),
72464 array(
72465 array('cli.php', '-f', '', 'foo'),
72466 array(new InputArgument('name'), new InputOption('foo', 'f', InputOption::VALUE_OPTIONAL)),
72467 array('foo' => ''),
72468 '->parse() parses short options with an optional empty value followed by an argument'
72469 ),
72470 array(
72471 array('cli.php', '-f', '', '-b'),
72472 array(new InputOption('foo', 'f', InputOption::VALUE_OPTIONAL), new InputOption('bar', 'b')),
72473 array('foo' => '', 'bar' => true),
72474 '->parse() parses short options with an optional empty value followed by an option'
72475 ),
72476 array(
72477 array('cli.php', '-f', '-b', 'foo'),
72478 array(new InputArgument('name'), new InputOption('foo', 'f', InputOption::VALUE_OPTIONAL), new InputOption('bar', 'b')),
72479 array('foo' => null, 'bar' => true),
72480 '->parse() parses short options with an optional value which is not present'
72481 ),
72482 array(
72483 array('cli.php', '-fb'),
72484 array(new InputOption('foo', 'f'), new InputOption('bar', 'b')),
72485 array('foo' => true, 'bar' => true),
72486 '->parse() parses short options when they are aggregated as a single one'
72487 ),
72488 array(
72489 array('cli.php', '-fb', 'bar'),
72490 array(new InputOption('foo', 'f'), new InputOption('bar', 'b', InputOption::VALUE_REQUIRED)),
72491 array('foo' => true, 'bar' => 'bar'),
72492 '->parse() parses short options when they are aggregated as a single one and the last one has a required value'
72493 ),
72494 array(
72495 array('cli.php', '-fb', 'bar'),
72496 array(new InputOption('foo', 'f'), new InputOption('bar', 'b', InputOption::VALUE_OPTIONAL)),
72497 array('foo' => true, 'bar' => 'bar'),
72498 '->parse() parses short options when they are aggregated as a single one and the last one has an optional value'
72499 ),
72500 array(
72501 array('cli.php', '-fbbar'),
72502 array(new InputOption('foo', 'f'), new InputOption('bar', 'b', InputOption::VALUE_OPTIONAL)),
72503 array('foo' => true, 'bar' => 'bar'),
72504 '->parse() parses short options when they are aggregated as a single one and the last one has an optional value with no separator'
72505 ),
72506 array(
72507 array('cli.php', '-fbbar'),
72508 array(new InputOption('foo', 'f', InputOption::VALUE_OPTIONAL), new InputOption('bar', 'b', InputOption::VALUE_OPTIONAL)),
72509 array('foo' => 'bbar', 'bar' => null),
72510 '->parse() parses short options when they are aggregated as a single one and one of them takes a value'
72511 )
72512 );
72513 }
72514
72515 /**
72516 * @dataProvider provideInvalidInput
72517 */
72518 public function testInvalidInput($argv, $definition, $expectedExceptionMessage)
72519 {
72520 $this->setExpectedException('RuntimeException', $expectedExceptionMessage);
72521
72522 $input = new ArgvInput($argv);
72523 $input->bind($definition);
72524 }
72525
72526 public function provideInvalidInput()
72527 {
72528 return array(
72529 array(
72530 array('cli.php', '--foo'),
72531 new InputDefinition(array(new InputOption('foo', 'f', InputOption::VALUE_REQUIRED))),
72532 'The "--foo" option requires a value.'
72533 ),
72534 array(
72535 array('cli.php', '-f'),
72536 new InputDefinition(array(new InputOption('foo', 'f', InputOption::VALUE_REQUIRED))),
72537 'The "--foo" option requires a value.'
72538 ),
72539 array(
72540 array('cli.php', '-ffoo'),
72541 new InputDefinition(array(new InputOption('foo', 'f', InputOption::VALUE_NONE))),
72542 'The "-o" option does not exist.'
72543 ),
72544 array(
72545 array('cli.php', '--foo=bar'),
72546 new InputDefinition(array(new InputOption('foo', 'f', InputOption::VALUE_NONE))),
72547 'The "--foo" option does not accept a value.'
72548 ),
72549 array(
72550 array('cli.php', 'foo', 'bar'),
72551 new InputDefinition(),
72552 'Too many arguments.'
72553 ),
72554 array(
72555 array('cli.php', '--foo'),
72556 new InputDefinition(),
72557 'The "--foo" option does not exist.'
72558 ),
72559 array(
72560 array('cli.php', '-f'),
72561 new InputDefinition(),
72562 'The "-f" option does not exist.'
72563 ),
72564 array(
72565 array('cli.php', '-1'),
72566 new InputDefinition(array(new InputArgument('number'))),
72567 'The "-1" option does not exist.'
72568 )
72569 );
72570 }
72571
72572 public function testParseArrayArgument()
72573 {
72574 $input = new ArgvInput(array('cli.php', 'foo', 'bar', 'baz', 'bat'));
72575 $input->bind(new InputDefinition(array(new InputArgument('name', InputArgument::IS_ARRAY))));
72576
72577 $this->assertEquals(array('name' => array('foo', 'bar', 'baz', 'bat')), $input->getArguments(), '->parse() parses array arguments');
72578 }
72579
72580 public function testParseArrayOption()
72581 {
72582 $input = new ArgvInput(array('cli.php', '--name=foo', '--name=bar', '--name=baz'));
72583 $input->bind(new InputDefinition(array(new InputOption('name', null, InputOption::VALUE_OPTIONAL | InputOption::VALUE_IS_ARRAY))));
72584
72585 $this->assertEquals(array('name' => array('foo', 'bar', 'baz')), $input->getOptions(), '->parse() parses array options ("--option=value" syntax)');
72586
72587 $input = new ArgvInput(array('cli.php', '--name', 'foo', '--name', 'bar', '--name', 'baz'));
72588 $input->bind(new InputDefinition(array(new InputOption('name', null, InputOption::VALUE_OPTIONAL | InputOption::VALUE_IS_ARRAY))));
72589 $this->assertEquals(array('name' => array('foo', 'bar', 'baz')), $input->getOptions(), '->parse() parses array options ("--option value" syntax)');
72590
72591 $input = new ArgvInput(array('cli.php', '--name=foo', '--name=bar', '--name='));
72592 $input->bind(new InputDefinition(array(new InputOption('name', null, InputOption::VALUE_OPTIONAL | InputOption::VALUE_IS_ARRAY))));
72593 $this->assertSame(array('name' => array('foo', 'bar', null)), $input->getOptions(), '->parse() parses empty array options as null ("--option=value" syntax)');
72594
72595 $input = new ArgvInput(array('cli.php', '--name', 'foo', '--name', 'bar', '--name', '--anotherOption'));
72596 $input->bind(new InputDefinition(array(
72597 new InputOption('name', null, InputOption::VALUE_OPTIONAL | InputOption::VALUE_IS_ARRAY),
72598 new InputOption('anotherOption', null, InputOption::VALUE_NONE),
72599 )));
72600 $this->assertSame(array('name' => array('foo', 'bar', null), 'anotherOption' => true), $input->getOptions(), '->parse() parses empty array options as null ("--option value" syntax)');
72601 }
72602
72603 public function testParseNegativeNumberAfterDoubleDash()
72604 {
72605 $input = new ArgvInput(array('cli.php', '--', '-1'));
72606 $input->bind(new InputDefinition(array(new InputArgument('number'))));
72607 $this->assertEquals(array('number' => '-1'), $input->getArguments(), '->parse() parses arguments with leading dashes as arguments after having encountered a double-dash sequence');
72608
72609 $input = new ArgvInput(array('cli.php', '-f', 'bar', '--', '-1'));
72610 $input->bind(new InputDefinition(array(new InputArgument('number'), new InputOption('foo', 'f', InputOption::VALUE_OPTIONAL))));
72611 $this->assertEquals(array('foo' => 'bar'), $input->getOptions(), '->parse() parses arguments with leading dashes as options before having encountered a double-dash sequence');
72612 $this->assertEquals(array('number' => '-1'), $input->getArguments(), '->parse() parses arguments with leading dashes as arguments after having encountered a double-dash sequence');
72613 }
72614
72615 public function testParseEmptyStringArgument()
72616 {
72617 $input = new ArgvInput(array('cli.php', '-f', 'bar', ''));
72618 $input->bind(new InputDefinition(array(new InputArgument('empty'), new InputOption('foo', 'f', InputOption::VALUE_OPTIONAL))));
72619
72620 $this->assertEquals(array('empty' => ''), $input->getArguments(), '->parse() parses empty string arguments');
72621 }
72622
72623 public function testGetFirstArgument()
72624 {
72625 $input = new ArgvInput(array('cli.php', '-fbbar'));
72626 $this->assertEquals('', $input->getFirstArgument(), '->getFirstArgument() returns the first argument from the raw input');
72627
72628 $input = new ArgvInput(array('cli.php', '-fbbar', 'foo'));
72629 $this->assertEquals('foo', $input->getFirstArgument(), '->getFirstArgument() returns the first argument from the raw input');
72630 }
72631
72632 public function testHasParameterOption()
72633 {
72634 $input = new ArgvInput(array('cli.php', '-f', 'foo'));
72635 $this->assertTrue($input->hasParameterOption('-f'), '->hasParameterOption() returns true if the given short option is in the raw input');
72636
72637 $input = new ArgvInput(array('cli.php', '--foo', 'foo'));
72638 $this->assertTrue($input->hasParameterOption('--foo'), '->hasParameterOption() returns true if the given short option is in the raw input');
72639
72640 $input = new ArgvInput(array('cli.php', 'foo'));
72641 $this->assertFalse($input->hasParameterOption('--foo'), '->hasParameterOption() returns false if the given short option is not in the raw input');
72642
72643 $input = new ArgvInput(array('cli.php', '--foo=bar'));
72644 $this->assertTrue($input->hasParameterOption('--foo'), '->hasParameterOption() returns true if the given option with provided value is in the raw input');
72645 }
72646
72647 public function testToString()
72648 {
72649 $input = new ArgvInput(array('cli.php', '-f', 'foo'));
72650 $this->assertEquals('-f foo', (string) $input);
72651
72652 $input = new ArgvInput(array('cli.php', '-f', '--bar=foo', 'a b c d', "A\nB'C"));
72653 $this->assertEquals('-f --bar=foo '.escapeshellarg('a b c d').' '.escapeshellarg("A\nB'C"), (string) $input);
72654 }
72655
72656 /**
72657 * @dataProvider provideGetParameterOptionValues
72658 */
72659 public function testGetParameterOptionEqualSign($argv, $key, $expected)
72660 {
72661 $input = new ArgvInput($argv);
72662 $this->assertEquals($expected, $input->getParameterOption($key), '->getParameterOption() returns the expected value');
72663 }
72664
72665 public function provideGetParameterOptionValues()
72666 {
72667 return array(
72668 array(array('app/console', 'foo:bar', '-e', 'dev'), '-e', 'dev'),
72669 array(array('app/console', 'foo:bar', '--env=dev'), '--env', 'dev'),
72670 array(array('app/console', 'foo:bar', '-e', 'dev'), array('-e', '--env'), 'dev'),
72671 array(array('app/console', 'foo:bar', '--env=dev'), array('-e', '--env'), 'dev'),
72672 array(array('app/console', 'foo:bar', '--env=dev', '--en=1'), array('--en'), '1'),
72673 );
72674 }
72675 }
72676 <?php
72677
72678 /*
72679 * This file is part of the Symfony package.
72680 *
72681 * (c) Fabien Potencier <fabien@symfony.com>
72682 *
72683 * For the full copyright and license information, please view the LICENSE
72684 * file that was distributed with this source code.
72685 */
72686
72687 namespace Symfony\Component\Console\Tests\Input;
72688
72689 use Symfony\Component\Console\Input\InputOption;
72690
72691 class InputOptionTest extends \PHPUnit_Framework_TestCase
72692 {
72693 public function testConstructor()
72694 {
72695 $option = new InputOption('foo');
72696 $this->assertEquals('foo', $option->getName(), '__construct() takes a name as its first argument');
72697 $option = new InputOption('--foo');
72698 $this->assertEquals('foo', $option->getName(), '__construct() removes the leading -- of the option name');
72699 }
72700
72701 /**
72702 * @expectedException \InvalidArgumentException
72703 * @expectedExceptionMessage Impossible to have an option mode VALUE_IS_ARRAY if the option does not accept a value.
72704 */
72705 public function testArrayModeWithoutValue()
72706 {
72707 new InputOption('foo', 'f', InputOption::VALUE_IS_ARRAY);
72708 }
72709
72710 public function testShortcut()
72711 {
72712 $option = new InputOption('foo', 'f');
72713 $this->assertEquals('f', $option->getShortcut(), '__construct() can take a shortcut as its second argument');
72714 $option = new InputOption('foo', '-f|-ff|fff');
72715 $this->assertEquals('f|ff|fff', $option->getShortcut(), '__construct() removes the leading - of the shortcuts');
72716 $option = new InputOption('foo', array('f', 'ff', '-fff'));
72717 $this->assertEquals('f|ff|fff', $option->getShortcut(), '__construct() removes the leading - of the shortcuts');
72718 $option = new InputOption('foo');
72719 $this->assertNull($option->getShortcut(), '__construct() makes the shortcut null by default');
72720 }
72721
72722 public function testModes()
72723 {
72724 $option = new InputOption('foo', 'f');
72725 $this->assertFalse($option->acceptValue(), '__construct() gives a "InputOption::VALUE_NONE" mode by default');
72726 $this->assertFalse($option->isValueRequired(), '__construct() gives a "InputOption::VALUE_NONE" mode by default');
72727 $this->assertFalse($option->isValueOptional(), '__construct() gives a "InputOption::VALUE_NONE" mode by default');
72728
72729 $option = new InputOption('foo', 'f', null);
72730 $this->assertFalse($option->acceptValue(), '__construct() can take "InputOption::VALUE_NONE" as its mode');
72731 $this->assertFalse($option->isValueRequired(), '__construct() can take "InputOption::VALUE_NONE" as its mode');
72732 $this->assertFalse($option->isValueOptional(), '__construct() can take "InputOption::VALUE_NONE" as its mode');
72733
72734 $option = new InputOption('foo', 'f', InputOption::VALUE_NONE);
72735 $this->assertFalse($option->acceptValue(), '__construct() can take "InputOption::VALUE_NONE" as its mode');
72736 $this->assertFalse($option->isValueRequired(), '__construct() can take "InputOption::VALUE_NONE" as its mode');
72737 $this->assertFalse($option->isValueOptional(), '__construct() can take "InputOption::VALUE_NONE" as its mode');
72738
72739 $option = new InputOption('foo', 'f', InputOption::VALUE_REQUIRED);
72740 $this->assertTrue($option->acceptValue(), '__construct() can take "InputOption::VALUE_REQUIRED" as its mode');
72741 $this->assertTrue($option->isValueRequired(), '__construct() can take "InputOption::VALUE_REQUIRED" as its mode');
72742 $this->assertFalse($option->isValueOptional(), '__construct() can take "InputOption::VALUE_REQUIRED" as its mode');
72743
72744 $option = new InputOption('foo', 'f', InputOption::VALUE_OPTIONAL);
72745 $this->assertTrue($option->acceptValue(), '__construct() can take "InputOption::VALUE_OPTIONAL" as its mode');
72746 $this->assertFalse($option->isValueRequired(), '__construct() can take "InputOption::VALUE_OPTIONAL" as its mode');
72747 $this->assertTrue($option->isValueOptional(), '__construct() can take "InputOption::VALUE_OPTIONAL" as its mode');
72748 }
72749
72750 /**
72751 * @dataProvider provideInvalidModes
72752 */
72753 public function testInvalidModes($mode)
72754 {
72755 $this->setExpectedException('InvalidArgumentException', sprintf('Option mode "%s" is not valid.', $mode));
72756
72757 new InputOption('foo', 'f', $mode);
72758 }
72759
72760 public function provideInvalidModes()
72761 {
72762 return array(
72763 array('ANOTHER_ONE'),
72764 array(-1)
72765 );
72766 }
72767
72768 /**
72769 * @expectedException \InvalidArgumentException
72770 */
72771 public function testEmptyNameIsInvalid()
72772 {
72773 new InputOption('');
72774 }
72775
72776 /**
72777 * @expectedException \InvalidArgumentException
72778 */
72779 public function testDoubleDashNameIsInvalid()
72780 {
72781 new InputOption('--');
72782 }
72783
72784 /**
72785 * @expectedException \InvalidArgumentException
72786 */
72787 public function testSingleDashOptionIsInvalid()
72788 {
72789 new InputOption('foo', '-');
72790 }
72791
72792 public function testIsArray()
72793 {
72794 $option = new InputOption('foo', null, InputOption::VALUE_OPTIONAL | InputOption::VALUE_IS_ARRAY);
72795 $this->assertTrue($option->isArray(), '->isArray() returns true if the option can be an array');
72796 $option = new InputOption('foo', null, InputOption::VALUE_NONE);
72797 $this->assertFalse($option->isArray(), '->isArray() returns false if the option can not be an array');
72798 }
72799
72800 public function testGetDescription()
72801 {
72802 $option = new InputOption('foo', 'f', null, 'Some description');
72803 $this->assertEquals('Some description', $option->getDescription(), '->getDescription() returns the description message');
72804 }
72805
72806 public function testGetDefault()
72807 {
72808 $option = new InputOption('foo', null, InputOption::VALUE_OPTIONAL, '', 'default');
72809 $this->assertEquals('default', $option->getDefault(), '->getDefault() returns the default value');
72810
72811 $option = new InputOption('foo', null, InputOption::VALUE_REQUIRED, '', 'default');
72812 $this->assertEquals('default', $option->getDefault(), '->getDefault() returns the default value');
72813
72814 $option = new InputOption('foo', null, InputOption::VALUE_REQUIRED);
72815 $this->assertNull($option->getDefault(), '->getDefault() returns null if no default value is configured');
72816
72817 $option = new InputOption('foo', null, InputOption::VALUE_OPTIONAL | InputOption::VALUE_IS_ARRAY);
72818 $this->assertEquals(array(), $option->getDefault(), '->getDefault() returns an empty array if option is an array');
72819
72820 $option = new InputOption('foo', null, InputOption::VALUE_NONE);
72821 $this->assertFalse($option->getDefault(), '->getDefault() returns false if the option does not take a value');
72822 }
72823
72824 public function testSetDefault()
72825 {
72826 $option = new InputOption('foo', null, InputOption::VALUE_REQUIRED, '', 'default');
72827 $option->setDefault(null);
72828 $this->assertNull($option->getDefault(), '->setDefault() can reset the default value by passing null');
72829 $option->setDefault('another');
72830 $this->assertEquals('another', $option->getDefault(), '->setDefault() changes the default value');
72831
72832 $option = new InputOption('foo', null, InputOption::VALUE_REQUIRED | InputOption::VALUE_IS_ARRAY);
72833 $option->setDefault(array(1, 2));
72834 $this->assertEquals(array(1, 2), $option->getDefault(), '->setDefault() changes the default value');
72835 }
72836
72837 /**
72838 * @expectedException \LogicException
72839 * @expectedExceptionMessage Cannot set a default value when using InputOption::VALUE_NONE mode.
72840 */
72841 public function testDefaultValueWithValueNoneMode()
72842 {
72843 $option = new InputOption('foo', 'f', InputOption::VALUE_NONE);
72844 $option->setDefault('default');
72845 }
72846
72847 /**
72848 * @expectedException \LogicException
72849 * @expectedExceptionMessage A default value for an array option must be an array.
72850 */
72851 public function testDefaultValueWithIsArrayMode()
72852 {
72853 $option = new InputOption('foo', 'f', InputOption::VALUE_OPTIONAL | InputOption::VALUE_IS_ARRAY);
72854 $option->setDefault('default');
72855 }
72856
72857 public function testEquals()
72858 {
72859 $option = new InputOption('foo', 'f', null, 'Some description');
72860 $option2 = new InputOption('foo', 'f', null, 'Alternative description');
72861 $this->assertTrue($option->equals($option2));
72862
72863 $option = new InputOption('foo', 'f', InputOption::VALUE_OPTIONAL, 'Some description');
72864 $option2 = new InputOption('foo', 'f', InputOption::VALUE_OPTIONAL, 'Some description', true);
72865 $this->assertFalse($option->equals($option2));
72866
72867 $option = new InputOption('foo', 'f', null, 'Some description');
72868 $option2 = new InputOption('bar', 'f', null, 'Some description');
72869 $this->assertFalse($option->equals($option2));
72870
72871 $option = new InputOption('foo', 'f', null, 'Some description');
72872 $option2 = new InputOption('foo', '', null, 'Some description');
72873 $this->assertFalse($option->equals($option2));
72874
72875 $option = new InputOption('foo', 'f', null, 'Some description');
72876 $option2 = new InputOption('foo', 'f', InputOption::VALUE_OPTIONAL, 'Some description');
72877 $this->assertFalse($option->equals($option2));
72878 }
72879 }
72880 <?php
72881
72882 /*
72883 * This file is part of the Symfony package.
72884 *
72885 * (c) Fabien Potencier <fabien@symfony.com>
72886 *
72887 * For the full copyright and license information, please view the LICENSE
72888 * file that was distributed with this source code.
72889 */
72890
72891 namespace Symfony\Component\Console\Tests\Input;
72892
72893 use Symfony\Component\Console\Input\InputArgument;
72894
72895 class InputArgumentTest extends \PHPUnit_Framework_TestCase
72896 {
72897 public function testConstructor()
72898 {
72899 $argument = new InputArgument('foo');
72900 $this->assertEquals('foo', $argument->getName(), '__construct() takes a name as its first argument');
72901 }
72902
72903 public function testModes()
72904 {
72905 $argument = new InputArgument('foo');
72906 $this->assertFalse($argument->isRequired(), '__construct() gives a "InputArgument::OPTIONAL" mode by default');
72907
72908 $argument = new InputArgument('foo', null);
72909 $this->assertFalse($argument->isRequired(), '__construct() can take "InputArgument::OPTIONAL" as its mode');
72910
72911 $argument = new InputArgument('foo', InputArgument::OPTIONAL);
72912 $this->assertFalse($argument->isRequired(), '__construct() can take "InputArgument::OPTIONAL" as its mode');
72913
72914 $argument = new InputArgument('foo', InputArgument::REQUIRED);
72915 $this->assertTrue($argument->isRequired(), '__construct() can take "InputArgument::REQUIRED" as its mode');
72916 }
72917
72918 /**
72919 * @dataProvider provideInvalidModes
72920 */
72921 public function testInvalidModes($mode)
72922 {
72923 $this->setExpectedException('InvalidArgumentException', sprintf('Argument mode "%s" is not valid.', $mode));
72924
72925 new InputArgument('foo', $mode);
72926 }
72927
72928 public function provideInvalidModes()
72929 {
72930 return array(
72931 array('ANOTHER_ONE'),
72932 array(-1)
72933 );
72934 }
72935
72936 public function testIsArray()
72937 {
72938 $argument = new InputArgument('foo', InputArgument::IS_ARRAY);
72939 $this->assertTrue($argument->isArray(), '->isArray() returns true if the argument can be an array');
72940 $argument = new InputArgument('foo', InputArgument::OPTIONAL | InputArgument::IS_ARRAY);
72941 $this->assertTrue($argument->isArray(), '->isArray() returns true if the argument can be an array');
72942 $argument = new InputArgument('foo', InputArgument::OPTIONAL);
72943 $this->assertFalse($argument->isArray(), '->isArray() returns false if the argument can not be an array');
72944 }
72945
72946 public function testGetDescription()
72947 {
72948 $argument = new InputArgument('foo', null, 'Some description');
72949 $this->assertEquals('Some description', $argument->getDescription(), '->getDescription() return the message description');
72950 }
72951
72952 public function testGetDefault()
72953 {
72954 $argument = new InputArgument('foo', InputArgument::OPTIONAL, '', 'default');
72955 $this->assertEquals('default', $argument->getDefault(), '->getDefault() return the default value');
72956 }
72957
72958 public function testSetDefault()
72959 {
72960 $argument = new InputArgument('foo', InputArgument::OPTIONAL, '', 'default');
72961 $argument->setDefault(null);
72962 $this->assertNull($argument->getDefault(), '->setDefault() can reset the default value by passing null');
72963 $argument->setDefault('another');
72964 $this->assertEquals('another', $argument->getDefault(), '->setDefault() changes the default value');
72965
72966 $argument = new InputArgument('foo', InputArgument::OPTIONAL | InputArgument::IS_ARRAY);
72967 $argument->setDefault(array(1, 2));
72968 $this->assertEquals(array(1, 2), $argument->getDefault(), '->setDefault() changes the default value');
72969 }
72970
72971 /**
72972 * @expectedException \LogicException
72973 * @expectedExceptionMessage Cannot set a default value except for InputArgument::OPTIONAL mode.
72974 */
72975 public function testSetDefaultWithRequiredArgument()
72976 {
72977 $argument = new InputArgument('foo', InputArgument::REQUIRED);
72978 $argument->setDefault('default');
72979 }
72980
72981 /**
72982 * @expectedException \LogicException
72983 * @expectedExceptionMessage A default value for an array argument must be an array.
72984 */
72985 public function testSetDefaultWithArrayArgument()
72986 {
72987 $argument = new InputArgument('foo', InputArgument::IS_ARRAY);
72988 $argument->setDefault('default');
72989 }
72990 }
72991 <?php
72992
72993 /*
72994 * This file is part of the Symfony package.
72995 *
72996 * (c) Fabien Potencier <fabien@symfony.com>
72997 *
72998 * For the full copyright and license information, please view the LICENSE
72999 * file that was distributed with this source code.
73000 */
73001
73002 namespace Symfony\Component\Console\Tests\Input;
73003
73004 use Symfony\Component\Console\Input\ArrayInput;
73005 use Symfony\Component\Console\Input\InputDefinition;
73006 use Symfony\Component\Console\Input\InputArgument;
73007 use Symfony\Component\Console\Input\InputOption;
73008
73009 class ArrayInputTest extends \PHPUnit_Framework_TestCase
73010 {
73011 public function testGetFirstArgument()
73012 {
73013 $input = new ArrayInput(array());
73014 $this->assertNull($input->getFirstArgument(), '->getFirstArgument() returns null if no argument were passed');
73015 $input = new ArrayInput(array('name' => 'Fabien'));
73016 $this->assertEquals('Fabien', $input->getFirstArgument(), '->getFirstArgument() returns the first passed argument');
73017 $input = new ArrayInput(array('--foo' => 'bar', 'name' => 'Fabien'));
73018 $this->assertEquals('Fabien', $input->getFirstArgument(), '->getFirstArgument() returns the first passed argument');
73019 }
73020
73021 public function testHasParameterOption()
73022 {
73023 $input = new ArrayInput(array('name' => 'Fabien', '--foo' => 'bar'));
73024 $this->assertTrue($input->hasParameterOption('--foo'), '->hasParameterOption() returns true if an option is present in the passed parameters');
73025 $this->assertFalse($input->hasParameterOption('--bar'), '->hasParameterOption() returns false if an option is not present in the passed parameters');
73026
73027 $input = new ArrayInput(array('--foo'));
73028 $this->assertTrue($input->hasParameterOption('--foo'), '->hasParameterOption() returns true if an option is present in the passed parameters');
73029 }
73030
73031 public function testParseArguments()
73032 {
73033 $input = new ArrayInput(array('name' => 'foo'), new InputDefinition(array(new InputArgument('name'))));
73034
73035 $this->assertEquals(array('name' => 'foo'), $input->getArguments(), '->parse() parses required arguments');
73036 }
73037
73038 /**
73039 * @dataProvider provideOptions
73040 */
73041 public function testParseOptions($input, $options, $expectedOptions, $message)
73042 {
73043 $input = new ArrayInput($input, new InputDefinition($options));
73044
73045 $this->assertEquals($expectedOptions, $input->getOptions(), $message);
73046 }
73047
73048 public function provideOptions()
73049 {
73050 return array(
73051 array(
73052 array('--foo' => 'bar'),
73053 array(new InputOption('foo')),
73054 array('foo' => 'bar'),
73055 '->parse() parses long options'
73056 ),
73057 array(
73058 array('--foo' => 'bar'),
73059 array(new InputOption('foo', 'f', InputOption::VALUE_OPTIONAL, '', 'default')),
73060 array('foo' => 'bar'),
73061 '->parse() parses long options with a default value'
73062 ),
73063 array(
73064 array('--foo' => null),
73065 array(new InputOption('foo', 'f', InputOption::VALUE_OPTIONAL, '', 'default')),
73066 array('foo' => 'default'),
73067 '->parse() parses long options with a default value'
73068 ),
73069 array(
73070 array('-f' => 'bar'),
73071 array(new InputOption('foo', 'f')),
73072 array('foo' => 'bar'),
73073 '->parse() parses short options'
73074 )
73075 );
73076 }
73077
73078 /**
73079 * @dataProvider provideInvalidInput
73080 */
73081 public function testParseInvalidInput($parameters, $definition, $expectedExceptionMessage)
73082 {
73083 $this->setExpectedException('InvalidArgumentException', $expectedExceptionMessage);
73084
73085 new ArrayInput($parameters, $definition);
73086 }
73087
73088 public function provideInvalidInput()
73089 {
73090 return array(
73091 array(
73092 array('foo' => 'foo'),
73093 new InputDefinition(array(new InputArgument('name'))),
73094 'The "foo" argument does not exist.'
73095 ),
73096 array(
73097 array('--foo' => null),
73098 new InputDefinition(array(new InputOption('foo', 'f', InputOption::VALUE_REQUIRED))),
73099 'The "--foo" option requires a value.'
73100 ),
73101 array(
73102 array('--foo' => 'foo'),
73103 new InputDefinition(),
73104 'The "--foo" option does not exist.'
73105 ),
73106 array(
73107 array('-o' => 'foo'),
73108 new InputDefinition(),
73109 'The "-o" option does not exist.'
73110 )
73111 );
73112 }
73113
73114 public function testToString()
73115 {
73116 $input = new ArrayInput(array('-f' => null, '-b' => 'bar', '--foo' => 'b a z', '--lala' => null, 'test' => 'Foo', 'test2' => "A\nB'C"));
73117 $this->assertEquals('-f -b=bar --foo='.escapeshellarg('b a z').' --lala Foo '.escapeshellarg("A\nB'C"), (string) $input);
73118 }
73119 }
73120 <?php
73121
73122 /*
73123 * This file is part of the Symfony package.
73124 *
73125 * (c) Fabien Potencier <fabien@symfony.com>
73126 *
73127 * For the full copyright and license information, please view the LICENSE
73128 * file that was distributed with this source code.
73129 */
73130
73131 namespace Symfony\Component\Console\Tests\Input;
73132
73133 use Symfony\Component\Console\Input\InputDefinition;
73134 use Symfony\Component\Console\Input\InputArgument;
73135 use Symfony\Component\Console\Input\InputOption;
73136
73137 class InputDefinitionTest extends \PHPUnit_Framework_TestCase
73138 {
73139 protected static $fixtures;
73140
73141 protected $foo, $bar, $foo1, $foo2;
73142
73143 public static function setUpBeforeClass()
73144 {
73145 self::$fixtures = __DIR__.'/../Fixtures/';
73146 }
73147
73148 public function testConstructorArguments()
73149 {
73150 $this->initializeArguments();
73151
73152 $definition = new InputDefinition();
73153 $this->assertEquals(array(), $definition->getArguments(), '__construct() creates a new InputDefinition object');
73154
73155 $definition = new InputDefinition(array($this->foo, $this->bar));
73156 $this->assertEquals(array('foo' => $this->foo, 'bar' => $this->bar), $definition->getArguments(), '__construct() takes an array of InputArgument objects as its first argument');
73157 }
73158
73159 public function testConstructorOptions()
73160 {
73161 $this->initializeOptions();
73162
73163 $definition = new InputDefinition();
73164 $this->assertEquals(array(), $definition->getOptions(), '__construct() creates a new InputDefinition object');
73165
73166 $definition = new InputDefinition(array($this->foo, $this->bar));
73167 $this->assertEquals(array('foo' => $this->foo, 'bar' => $this->bar), $definition->getOptions(), '__construct() takes an array of InputOption objects as its first argument');
73168 }
73169
73170 public function testSetArguments()
73171 {
73172 $this->initializeArguments();
73173
73174 $definition = new InputDefinition();
73175 $definition->setArguments(array($this->foo));
73176 $this->assertEquals(array('foo' => $this->foo), $definition->getArguments(), '->setArguments() sets the array of InputArgument objects');
73177 $definition->setArguments(array($this->bar));
73178
73179 $this->assertEquals(array('bar' => $this->bar), $definition->getArguments(), '->setArguments() clears all InputArgument objects');
73180 }
73181
73182 public function testAddArguments()
73183 {
73184 $this->initializeArguments();
73185
73186 $definition = new InputDefinition();
73187 $definition->addArguments(array($this->foo));
73188 $this->assertEquals(array('foo' => $this->foo), $definition->getArguments(), '->addArguments() adds an array of InputArgument objects');
73189 $definition->addArguments(array($this->bar));
73190 $this->assertEquals(array('foo' => $this->foo, 'bar' => $this->bar), $definition->getArguments(), '->addArguments() does not clear existing InputArgument objects');
73191 }
73192
73193 public function testAddArgument()
73194 {
73195 $this->initializeArguments();
73196
73197 $definition = new InputDefinition();
73198 $definition->addArgument($this->foo);
73199 $this->assertEquals(array('foo' => $this->foo), $definition->getArguments(), '->addArgument() adds a InputArgument object');
73200 $definition->addArgument($this->bar);
73201 $this->assertEquals(array('foo' => $this->foo, 'bar' => $this->bar), $definition->getArguments(), '->addArgument() adds a InputArgument object');
73202 }
73203
73204 /**
73205 * @expectedException \LogicException
73206 * @expectedExceptionMessage An argument with name "foo" already exists.
73207 */
73208 public function testArgumentsMustHaveDifferentNames()
73209 {
73210 $this->initializeArguments();
73211
73212 $definition = new InputDefinition();
73213 $definition->addArgument($this->foo);
73214 $definition->addArgument($this->foo1);
73215 }
73216
73217 /**
73218 * @expectedException \LogicException
73219 * @expectedExceptionMessage Cannot add an argument after an array argument.
73220 */
73221 public function testArrayArgumentHasToBeLast()
73222 {
73223 $this->initializeArguments();
73224
73225 $definition = new InputDefinition();
73226 $definition->addArgument(new InputArgument('fooarray', InputArgument::IS_ARRAY));
73227 $definition->addArgument(new InputArgument('anotherbar'));
73228 }
73229
73230 /**
73231 * @expectedException \LogicException
73232 * @expectedExceptionMessage Cannot add a required argument after an optional one.
73233 */
73234 public function testRequiredArgumentCannotFollowAnOptionalOne()
73235 {
73236 $this->initializeArguments();
73237
73238 $definition = new InputDefinition();
73239 $definition->addArgument($this->foo);
73240 $definition->addArgument($this->foo2);
73241 }
73242
73243 public function testGetArgument()
73244 {
73245 $this->initializeArguments();
73246
73247 $definition = new InputDefinition();
73248 $definition->addArguments(array($this->foo));
73249 $this->assertEquals($this->foo, $definition->getArgument('foo'), '->getArgument() returns a InputArgument by its name');
73250 }
73251
73252 /**
73253 * @expectedException \InvalidArgumentException
73254 * @expectedExceptionMessage The "bar" argument does not exist.
73255 */
73256 public function testGetInvalidArgument()
73257 {
73258 $this->initializeArguments();
73259
73260 $definition = new InputDefinition();
73261 $definition->addArguments(array($this->foo));
73262 $definition->getArgument('bar');
73263 }
73264
73265 public function testHasArgument()
73266 {
73267 $this->initializeArguments();
73268
73269 $definition = new InputDefinition();
73270 $definition->addArguments(array($this->foo));
73271
73272 $this->assertTrue($definition->hasArgument('foo'), '->hasArgument() returns true if a InputArgument exists for the given name');
73273 $this->assertFalse($definition->hasArgument('bar'), '->hasArgument() returns false if a InputArgument exists for the given name');
73274 }
73275
73276 public function testGetArgumentRequiredCount()
73277 {
73278 $this->initializeArguments();
73279
73280 $definition = new InputDefinition();
73281 $definition->addArgument($this->foo2);
73282 $this->assertEquals(1, $definition->getArgumentRequiredCount(), '->getArgumentRequiredCount() returns the number of required arguments');
73283 $definition->addArgument($this->foo);
73284 $this->assertEquals(1, $definition->getArgumentRequiredCount(), '->getArgumentRequiredCount() returns the number of required arguments');
73285 }
73286
73287 public function testGetArgumentCount()
73288 {
73289 $this->initializeArguments();
73290
73291 $definition = new InputDefinition();
73292 $definition->addArgument($this->foo2);
73293 $this->assertEquals(1, $definition->getArgumentCount(), '->getArgumentCount() returns the number of arguments');
73294 $definition->addArgument($this->foo);
73295 $this->assertEquals(2, $definition->getArgumentCount(), '->getArgumentCount() returns the number of arguments');
73296 }
73297
73298 public function testGetArgumentDefaults()
73299 {
73300 $definition = new InputDefinition(array(
73301 new InputArgument('foo1', InputArgument::OPTIONAL),
73302 new InputArgument('foo2', InputArgument::OPTIONAL, '', 'default'),
73303 new InputArgument('foo3', InputArgument::OPTIONAL | InputArgument::IS_ARRAY),
73304 // new InputArgument('foo4', InputArgument::OPTIONAL | InputArgument::IS_ARRAY, '', array(1, 2)),
73305 ));
73306 $this->assertEquals(array('foo1' => null, 'foo2' => 'default', 'foo3' => array()), $definition->getArgumentDefaults(), '->getArgumentDefaults() return the default values for each argument');
73307
73308 $definition = new InputDefinition(array(
73309 new InputArgument('foo4', InputArgument::OPTIONAL | InputArgument::IS_ARRAY, '', array(1, 2)),
73310 ));
73311 $this->assertEquals(array('foo4' => array(1, 2)), $definition->getArgumentDefaults(), '->getArgumentDefaults() return the default values for each argument');
73312 }
73313
73314 public function testSetOptions()
73315 {
73316 $this->initializeOptions();
73317
73318 $definition = new InputDefinition(array($this->foo));
73319 $this->assertEquals(array('foo' => $this->foo), $definition->getOptions(), '->setOptions() sets the array of InputOption objects');
73320 $definition->setOptions(array($this->bar));
73321 $this->assertEquals(array('bar' => $this->bar), $definition->getOptions(), '->setOptions() clears all InputOption objects');
73322 }
73323
73324 /**
73325 * @expectedException \InvalidArgumentException
73326 * @expectedExceptionMessage The "-f" option does not exist.
73327 */
73328 public function testSetOptionsClearsOptions()
73329 {
73330 $this->initializeOptions();
73331
73332 $definition = new InputDefinition(array($this->foo));
73333 $definition->setOptions(array($this->bar));
73334 $definition->getOptionForShortcut('f');
73335 }
73336
73337 public function testAddOptions()
73338 {
73339 $this->initializeOptions();
73340
73341 $definition = new InputDefinition(array($this->foo));
73342 $this->assertEquals(array('foo' => $this->foo), $definition->getOptions(), '->addOptions() adds an array of InputOption objects');
73343 $definition->addOptions(array($this->bar));
73344 $this->assertEquals(array('foo' => $this->foo, 'bar' => $this->bar), $definition->getOptions(), '->addOptions() does not clear existing InputOption objects');
73345 }
73346
73347 public function testAddOption()
73348 {
73349 $this->initializeOptions();
73350
73351 $definition = new InputDefinition();
73352 $definition->addOption($this->foo);
73353 $this->assertEquals(array('foo' => $this->foo), $definition->getOptions(), '->addOption() adds a InputOption object');
73354 $definition->addOption($this->bar);
73355 $this->assertEquals(array('foo' => $this->foo, 'bar' => $this->bar), $definition->getOptions(), '->addOption() adds a InputOption object');
73356 }
73357
73358 /**
73359 * @expectedException \LogicException
73360 * @expectedExceptionMessage An option named "foo" already exists.
73361 */
73362 public function testAddDuplicateOption()
73363 {
73364 $this->initializeOptions();
73365
73366 $definition = new InputDefinition();
73367 $definition->addOption($this->foo);
73368 $definition->addOption($this->foo2);
73369 }
73370
73371 /**
73372 * @expectedException \LogicException
73373 * @expectedExceptionMessage An option with shortcut "f" already exists.
73374 */
73375 public function testAddDuplicateShortcutOption()
73376 {
73377 $this->initializeOptions();
73378
73379 $definition = new InputDefinition();
73380 $definition->addOption($this->foo);
73381 $definition->addOption($this->foo1);
73382 }
73383
73384 public function testGetOption()
73385 {
73386 $this->initializeOptions();
73387
73388 $definition = new InputDefinition(array($this->foo));
73389 $this->assertEquals($this->foo, $definition->getOption('foo'), '->getOption() returns a InputOption by its name');
73390 }
73391
73392 /**
73393 * @expectedException \InvalidArgumentException
73394 * @expectedExceptionMessage The "--bar" option does not exist.
73395 */
73396 public function testGetInvalidOption()
73397 {
73398 $this->initializeOptions();
73399
73400 $definition = new InputDefinition(array($this->foo));
73401 $definition->getOption('bar');
73402 }
73403
73404 public function testHasOption()
73405 {
73406 $this->initializeOptions();
73407
73408 $definition = new InputDefinition(array($this->foo));
73409 $this->assertTrue($definition->hasOption('foo'), '->hasOption() returns true if a InputOption exists for the given name');
73410 $this->assertFalse($definition->hasOption('bar'), '->hasOption() returns false if a InputOption exists for the given name');
73411 }
73412
73413 public function testHasShortcut()
73414 {
73415 $this->initializeOptions();
73416
73417 $definition = new InputDefinition(array($this->foo));
73418 $this->assertTrue($definition->hasShortcut('f'), '->hasShortcut() returns true if a InputOption exists for the given shortcut');
73419 $this->assertFalse($definition->hasShortcut('b'), '->hasShortcut() returns false if a InputOption exists for the given shortcut');
73420 }
73421
73422 public function testGetOptionForShortcut()
73423 {
73424 $this->initializeOptions();
73425
73426 $definition = new InputDefinition(array($this->foo));
73427 $this->assertEquals($this->foo, $definition->getOptionForShortcut('f'), '->getOptionForShortcut() returns a InputOption by its shortcut');
73428 }
73429
73430 public function testGetOptionForMultiShortcut()
73431 {
73432 $this->initializeOptions();
73433
73434 $definition = new InputDefinition(array($this->multi));
73435 $this->assertEquals($this->multi, $definition->getOptionForShortcut('m'), '->getOptionForShortcut() returns a InputOption by its shortcut');
73436 $this->assertEquals($this->multi, $definition->getOptionForShortcut('mmm'), '->getOptionForShortcut() returns a InputOption by its shortcut');
73437 }
73438
73439 /**
73440 * @expectedException \InvalidArgumentException
73441 * @expectedExceptionMessage The "-l" option does not exist.
73442 */
73443 public function testGetOptionForInvalidShortcut()
73444 {
73445 $this->initializeOptions();
73446
73447 $definition = new InputDefinition(array($this->foo));
73448 $definition->getOptionForShortcut('l');
73449 }
73450
73451 public function testGetOptionDefaults()
73452 {
73453 $definition = new InputDefinition(array(
73454 new InputOption('foo1', null, InputOption::VALUE_NONE),
73455 new InputOption('foo2', null, InputOption::VALUE_REQUIRED),
73456 new InputOption('foo3', null, InputOption::VALUE_REQUIRED, '', 'default'),
73457 new InputOption('foo4', null, InputOption::VALUE_OPTIONAL),
73458 new InputOption('foo5', null, InputOption::VALUE_OPTIONAL, '', 'default'),
73459 new InputOption('foo6', null, InputOption::VALUE_OPTIONAL | InputOption::VALUE_IS_ARRAY),
73460 new InputOption('foo7', null, InputOption::VALUE_OPTIONAL | InputOption::VALUE_IS_ARRAY, '', array(1, 2)),
73461 ));
73462 $defaults = array(
73463 'foo1' => null,
73464 'foo2' => null,
73465 'foo3' => 'default',
73466 'foo4' => null,
73467 'foo5' => 'default',
73468 'foo6' => array(),
73469 'foo7' => array(1, 2),
73470 );
73471 $this->assertEquals($defaults, $definition->getOptionDefaults(), '->getOptionDefaults() returns the default values for all options');
73472 }
73473
73474 public function testGetSynopsis()
73475 {
73476 $definition = new InputDefinition(array(new InputOption('foo')));
73477 $this->assertEquals('[--foo]', $definition->getSynopsis(), '->getSynopsis() returns a synopsis of arguments and options');
73478 $definition = new InputDefinition(array(new InputOption('foo', 'f')));
73479 $this->assertEquals('[-f|--foo]', $definition->getSynopsis(), '->getSynopsis() returns a synopsis of arguments and options');
73480 $definition = new InputDefinition(array(new InputOption('foo', 'f', InputOption::VALUE_REQUIRED)));
73481 $this->assertEquals('[-f|--foo="..."]', $definition->getSynopsis(), '->getSynopsis() returns a synopsis of arguments and options');
73482 $definition = new InputDefinition(array(new InputOption('foo', 'f', InputOption::VALUE_OPTIONAL)));
73483 $this->assertEquals('[-f|--foo[="..."]]', $definition->getSynopsis(), '->getSynopsis() returns a synopsis of arguments and options');
73484
73485 $definition = new InputDefinition(array(new InputArgument('foo')));
73486 $this->assertEquals('[foo]', $definition->getSynopsis(), '->getSynopsis() returns a synopsis of arguments and options');
73487 $definition = new InputDefinition(array(new InputArgument('foo', InputArgument::REQUIRED)));
73488 $this->assertEquals('foo', $definition->getSynopsis(), '->getSynopsis() returns a synopsis of arguments and options');
73489 $definition = new InputDefinition(array(new InputArgument('foo', InputArgument::IS_ARRAY)));
73490 $this->assertEquals('[foo1] ... [fooN]', $definition->getSynopsis(), '->getSynopsis() returns a synopsis of arguments and options');
73491 $definition = new InputDefinition(array(new InputArgument('foo', InputArgument::REQUIRED | InputArgument::IS_ARRAY)));
73492 $this->assertEquals('foo1 ... [fooN]', $definition->getSynopsis(), '->getSynopsis() returns a synopsis of arguments and options');
73493 }
73494
73495 public function testAsText()
73496 {
73497 $definition = new InputDefinition(array(
73498 new InputArgument('foo', InputArgument::OPTIONAL, 'The foo argument'),
73499 new InputArgument('baz', InputArgument::OPTIONAL, 'The baz argument', true),
73500 new InputArgument('bar', InputArgument::OPTIONAL | InputArgument::IS_ARRAY, 'The bar argument', array('http://foo.com/')),
73501 new InputOption('foo', 'f', InputOption::VALUE_REQUIRED, 'The foo option'),
73502 new InputOption('baz', null, InputOption::VALUE_OPTIONAL, 'The baz option', false),
73503 new InputOption('bar', 'b', InputOption::VALUE_OPTIONAL, 'The bar option', 'bar'),
73504 new InputOption('qux', '', InputOption::VALUE_OPTIONAL | InputOption::VALUE_IS_ARRAY, 'The qux option', array('http://foo.com/', 'bar')),
73505 new InputOption('qux2', '', InputOption::VALUE_OPTIONAL | InputOption::VALUE_IS_ARRAY, 'The qux2 option', array('foo' => 'bar')),
73506 ));
73507 $this->assertStringEqualsFile(self::$fixtures.'/definition_astext.txt', $definition->asText(), '->asText() returns a textual representation of the InputDefinition');
73508 }
73509
73510 public function testAsXml()
73511 {
73512 $definition = new InputDefinition(array(
73513 new InputArgument('foo', InputArgument::OPTIONAL, 'The foo argument'),
73514 new InputArgument('baz', InputArgument::OPTIONAL, 'The baz argument', true),
73515 new InputArgument('bar', InputArgument::OPTIONAL | InputArgument::IS_ARRAY, 'The bar argument', array('bar')),
73516 new InputOption('foo', 'f', InputOption::VALUE_REQUIRED, 'The foo option'),
73517 new InputOption('baz', null, InputOption::VALUE_OPTIONAL, 'The baz option', false),
73518 new InputOption('bar', 'b', InputOption::VALUE_OPTIONAL, 'The bar option', 'bar'),
73519 ));
73520 $this->assertXmlStringEqualsXmlFile(self::$fixtures.'/definition_asxml.txt', $definition->asXml(), '->asText() returns a textual representation of the InputDefinition');
73521 }
73522
73523 protected function initializeArguments()
73524 {
73525 $this->foo = new InputArgument('foo');
73526 $this->bar = new InputArgument('bar');
73527 $this->foo1 = new InputArgument('foo');
73528 $this->foo2 = new InputArgument('foo2', InputArgument::REQUIRED);
73529 }
73530
73531 protected function initializeOptions()
73532 {
73533 $this->foo = new InputOption('foo', 'f');
73534 $this->bar = new InputOption('bar', 'b');
73535 $this->foo1 = new InputOption('fooBis', 'f');
73536 $this->foo2 = new InputOption('foo', 'p');
73537 $this->multi = new InputOption('multi', 'm|mm|mmm');
73538 }
73539 }
73540 <?php
73541
73542 /*
73543 * This file is part of the Symfony package.
73544 *
73545 * (c) Fabien Potencier <fabien@symfony.com>
73546 *
73547 * For the full copyright and license information, please view the LICENSE
73548 * file that was distributed with this source code.
73549 */
73550
73551 namespace Symfony\Component\Console\Tests\Input;
73552
73553 use Symfony\Component\Console\Input\InputDefinition;
73554 use Symfony\Component\Console\Input\InputOption;
73555 use Symfony\Component\Console\Input\StringInput;
73556
73557 class StringInputTest extends \PHPUnit_Framework_TestCase
73558 {
73559 /**
73560 * @dataProvider getTokenizeData
73561 */
73562 public function testTokenize($input, $tokens, $message)
73563 {
73564 $input = new StringInput($input);
73565 $r = new \ReflectionClass('Symfony\Component\Console\Input\ArgvInput');
73566 $p = $r->getProperty('tokens');
73567 $p->setAccessible(true);
73568 $this->assertEquals($tokens, $p->getValue($input), $message);
73569 }
73570
73571 public function testInputOptionWithGivenString()
73572 {
73573 $definition = new InputDefinition(
73574 array(new InputOption('foo', null, InputOption::VALUE_REQUIRED))
73575 );
73576
73577 // call to bind
73578 $input = new StringInput('--foo=bar');
73579 $input->bind($definition);
73580 $this->assertEquals('bar', $input->getOption('foo'));
73581
73582 // definition in constructor
73583 $input = new StringInput('--foo=bar', $definition);
73584 $this->assertEquals('bar', $input->getOption('foo'));
73585 }
73586
73587 public function getTokenizeData()
73588 {
73589 return array(
73590 array('', array(), '->tokenize() parses an empty string'),
73591 array('foo', array('foo'), '->tokenize() parses arguments'),
73592 array(' foo bar ', array('foo', 'bar'), '->tokenize() ignores whitespaces between arguments'),
73593 array('"quoted"', array('quoted'), '->tokenize() parses quoted arguments'),
73594 array("'quoted'", array('quoted'), '->tokenize() parses quoted arguments'),
73595 array("'a\rb\nc\td'", array("a\rb\nc\td"), '->tokenize() parses whitespace chars in strings'),
73596 array("'a'\r'b'\n'c'\t'd'", array('a','b','c','d'), '->tokenize() parses whitespace chars between args as spaces'),
73597 array('\"quoted\"', array('"quoted"'), '->tokenize() parses escaped-quoted arguments'),
73598 array("\'quoted\'", array('\'quoted\''), '->tokenize() parses escaped-quoted arguments'),
73599 array('-a', array('-a'), '->tokenize() parses short options'),
73600 array('-azc', array('-azc'), '->tokenize() parses aggregated short options'),
73601 array('-awithavalue', array('-awithavalue'), '->tokenize() parses short options with a value'),
73602 array('-a"foo bar"', array('-afoo bar'), '->tokenize() parses short options with a value'),
73603 array('-a"foo bar""foo bar"', array('-afoo barfoo bar'), '->tokenize() parses short options with a value'),
73604 array('-a\'foo bar\'', array('-afoo bar'), '->tokenize() parses short options with a value'),
73605 array('-a\'foo bar\'\'foo bar\'', array('-afoo barfoo bar'), '->tokenize() parses short options with a value'),
73606 array('-a\'foo bar\'"foo bar"', array('-afoo barfoo bar'), '->tokenize() parses short options with a value'),
73607 array('--long-option', array('--long-option'), '->tokenize() parses long options'),
73608 array('--long-option=foo', array('--long-option=foo'), '->tokenize() parses long options with a value'),
73609 array('--long-option="foo bar"', array('--long-option=foo bar'), '->tokenize() parses long options with a value'),
73610 array('--long-option="foo bar""another"', array('--long-option=foo baranother'), '->tokenize() parses long options with a value'),
73611 array('--long-option=\'foo bar\'', array('--long-option=foo bar'), '->tokenize() parses long options with a value'),
73612 array("--long-option='foo bar''another'", array("--long-option=foo baranother"), '->tokenize() parses long options with a value'),
73613 array("--long-option='foo bar'\"another\"", array("--long-option=foo baranother"), '->tokenize() parses long options with a value'),
73614 array('foo -a -ffoo --long bar', array('foo', '-a', '-ffoo', '--long', 'bar'), '->tokenize() parses when several arguments and options'),
73615 );
73616 }
73617
73618 public function testToString()
73619 {
73620 $input = new StringInput('-f foo');
73621 $this->assertEquals('-f foo', (string) $input);
73622
73623 $input = new StringInput('-f --bar=foo "a b c d"');
73624 $this->assertEquals('-f --bar=foo '.escapeshellarg('a b c d'), (string) $input);
73625
73626 $input = new StringInput('-f --bar=foo \'a b c d\' '."'A\nB\\'C'");
73627 $this->assertEquals('-f --bar=foo '.escapeshellarg('a b c d').' '.escapeshellarg("A\nB'C"), (string) $input);
73628 }
73629 }
73630 <?php
73631
73632 /*
73633 * This file is part of the Symfony package.
73634 *
73635 * (c) Fabien Potencier <fabien@symfony.com>
73636 *
73637 * For the full copyright and license information, please view the LICENSE
73638 * file that was distributed with this source code.
73639 */
73640
73641 namespace Symfony\Component\Console\Tests\Input;
73642
73643 use Symfony\Component\Console\Input\ArrayInput;
73644 use Symfony\Component\Console\Input\InputDefinition;
73645 use Symfony\Component\Console\Input\InputArgument;
73646 use Symfony\Component\Console\Input\InputOption;
73647
73648 class InputTest extends \PHPUnit_Framework_TestCase
73649 {
73650 public function testConstructor()
73651 {
73652 $input = new ArrayInput(array('name' => 'foo'), new InputDefinition(array(new InputArgument('name'))));
73653 $this->assertEquals('foo', $input->getArgument('name'), '->__construct() takes a InputDefinition as an argument');
73654 }
73655
73656 public function testOptions()
73657 {
73658 $input = new ArrayInput(array('--name' => 'foo'), new InputDefinition(array(new InputOption('name'))));
73659 $this->assertEquals('foo', $input->getOption('name'), '->getOption() returns the value for the given option');
73660
73661 $input->setOption('name', 'bar');
73662 $this->assertEquals('bar', $input->getOption('name'), '->setOption() sets the value for a given option');
73663 $this->assertEquals(array('name' => 'bar'), $input->getOptions(), '->getOptions() returns all option values');
73664
73665 $input = new ArrayInput(array('--name' => 'foo'), new InputDefinition(array(new InputOption('name'), new InputOption('bar', '', InputOption::VALUE_OPTIONAL, '', 'default'))));
73666 $this->assertEquals('default', $input->getOption('bar'), '->getOption() returns the default value for optional options');
73667 $this->assertEquals(array('name' => 'foo', 'bar' => 'default'), $input->getOptions(), '->getOptions() returns all option values, even optional ones');
73668 }
73669
73670 /**
73671 * @expectedException \InvalidArgumentException
73672 * @expectedExceptionMessage The "foo" option does not exist.
73673 */
73674 public function testSetInvalidOption()
73675 {
73676 $input = new ArrayInput(array('--name' => 'foo'), new InputDefinition(array(new InputOption('name'), new InputOption('bar', '', InputOption::VALUE_OPTIONAL, '', 'default'))));
73677 $input->setOption('foo', 'bar');
73678 }
73679
73680 /**
73681 * @expectedException \InvalidArgumentException
73682 * @expectedExceptionMessage The "foo" option does not exist.
73683 */
73684 public function testGetInvalidOption()
73685 {
73686 $input = new ArrayInput(array('--name' => 'foo'), new InputDefinition(array(new InputOption('name'), new InputOption('bar', '', InputOption::VALUE_OPTIONAL, '', 'default'))));
73687 $input->getOption('foo');
73688 }
73689
73690 public function testArguments()
73691 {
73692 $input = new ArrayInput(array('name' => 'foo'), new InputDefinition(array(new InputArgument('name'))));
73693 $this->assertEquals('foo', $input->getArgument('name'), '->getArgument() returns the value for the given argument');
73694
73695 $input->setArgument('name', 'bar');
73696 $this->assertEquals('bar', $input->getArgument('name'), '->setArgument() sets the value for a given argument');
73697 $this->assertEquals(array('name' => 'bar'), $input->getArguments(), '->getArguments() returns all argument values');
73698
73699 $input = new ArrayInput(array('name' => 'foo'), new InputDefinition(array(new InputArgument('name'), new InputArgument('bar', InputArgument::OPTIONAL, '', 'default'))));
73700 $this->assertEquals('default', $input->getArgument('bar'), '->getArgument() returns the default value for optional arguments');
73701 $this->assertEquals(array('name' => 'foo', 'bar' => 'default'), $input->getArguments(), '->getArguments() returns all argument values, even optional ones');
73702 }
73703
73704 /**
73705 * @expectedException \InvalidArgumentException
73706 * @expectedExceptionMessage The "foo" argument does not exist.
73707 */
73708 public function testSetInvalidArgument()
73709 {
73710 $input = new ArrayInput(array('name' => 'foo'), new InputDefinition(array(new InputArgument('name'), new InputArgument('bar', InputArgument::OPTIONAL, '', 'default'))));
73711 $input->setArgument('foo', 'bar');
73712 }
73713
73714 /**
73715 * @expectedException \InvalidArgumentException
73716 * @expectedExceptionMessage The "foo" argument does not exist.
73717 */
73718 public function testGetInvalidArgument()
73719 {
73720 $input = new ArrayInput(array('name' => 'foo'), new InputDefinition(array(new InputArgument('name'), new InputArgument('bar', InputArgument::OPTIONAL, '', 'default'))));
73721 $input->getArgument('foo');
73722 }
73723
73724 /**
73725 * @expectedException \RuntimeException
73726 * @expectedExceptionMessage Not enough arguments.
73727 */
73728 public function testValidateWithMissingArguments()
73729 {
73730 $input = new ArrayInput(array());
73731 $input->bind(new InputDefinition(array(new InputArgument('name', InputArgument::REQUIRED))));
73732 $input->validate();
73733 }
73734
73735 public function testValidate()
73736 {
73737 $input = new ArrayInput(array('name' => 'foo'));
73738 $input->bind(new InputDefinition(array(new InputArgument('name', InputArgument::REQUIRED))));
73739
73740 $this->assertNull($input->validate());
73741 }
73742
73743 public function testSetGetInteractive()
73744 {
73745 $input = new ArrayInput(array());
73746 $this->assertTrue($input->isInteractive(), '->isInteractive() returns whether the input should be interactive or not');
73747 $input->setInteractive(false);
73748 $this->assertFalse($input->isInteractive(), '->setInteractive() changes the interactive flag');
73749 }
73750 }
73751 Console Component
73752 =================
73753
73754 Console eases the creation of beautiful and testable command line interfaces.
73755
73756 The Application object manages the CLI application:
73757
73758 use Symfony\Component\Console\Application;
73759
73760 $console = new Application();
73761 $console->run();
73762
73763 The ``run()`` method parses the arguments and options passed on the command
73764 line and executes the right command.
73765
73766 Registering a new command can easily be done via the ``register()`` method,
73767 which returns a ``Command`` instance:
73768
73769 use Symfony\Component\Console\Input\InputInterface;
73770 use Symfony\Component\Console\Input\InputArgument;
73771 use Symfony\Component\Console\Input\InputOption;
73772 use Symfony\Component\Console\Output\OutputInterface;
73773
73774 $console
73775 ->register('ls')
73776 ->setDefinition(array(
73777 new InputArgument('dir', InputArgument::REQUIRED, 'Directory name'),
73778 ))
73779 ->setDescription('Displays the files in the given directory')
73780 ->setCode(function (InputInterface $input, OutputInterface $output) {
73781 $dir = $input->getArgument('dir');
73782
73783 $output->writeln(sprintf('Dir listing for <info>%s</info>', $dir));
73784 })
73785 ;
73786
73787 You can also register new commands via classes.
73788
73789 The component provides a lot of features like output coloring, input and
73790 output abstractions (so that you can easily unit-test your commands),
73791 validation, automatic help messages, ...
73792
73793 Tests
73794 -----
73795
73796 You can run the unit tests with the following command:
73797
73798 $ cd path/to/Symfony/Component/Console/
73799 $ composer.phar install --dev
73800 $ phpunit
73801
73802 Third Party
73803 -----------
73804
73805 `Resources/bin/hiddeninput.exe` is a third party binary provided within this
73806 component. Find sources and license at https://github.com/Seldaek/hidden-input.
73807
73808 Resources
73809 ---------
73810
73811 [The Console Component](http://symfony.com/doc/current/components/console.html)
73812
73813 [How to create a Console Command](http://symfony.com/doc/current/cookbook/console/console_command.html)
73814 Copyright (c) 2004-2013 Fabien Potencier
73815
73816 Permission is hereby granted, free of charge, to any person obtaining a copy
73817 of this software and associated documentation files (the "Software"), to deal
73818 in the Software without restriction, including without limitation the rights
73819 to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
73820 copies of the Software, and to permit persons to whom the Software is furnished
73821 to do so, subject to the following conditions:
73822
73823 The above copyright notice and this permission notice shall be included in all
73824 copies or substantial portions of the Software.
73825
73826 THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
73827 IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
73828 FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
73829 AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
73830 LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
73831 OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
73832 THE SOFTWARE.
73833 <?php
73834
73835 /*
73836 * This file is part of the Symfony package.
73837 *
73838 * (c) Fabien Potencier <fabien@symfony.com>
73839 *
73840 * For the full copyright and license information, please view the LICENSE
73841 * file that was distributed with this source code.
73842 */
73843
73844 namespace Symfony\Component\Console\Event;
73845
73846 use Symfony\Component\Console\Command\Command;
73847 use Symfony\Component\Console\Input\InputInterface;
73848 use Symfony\Component\Console\Output\OutputInterface;
73849
73850 /**
73851 * Allows to manipulate the exit code of a command after its execution.
73852 *
73853 * @author Francesco Levorato <git@flevour.net>
73854 */
73855 class ConsoleTerminateEvent extends ConsoleEvent
73856 {
73857 /**
73858 * The exit code of the command.
73859 *
73860 * @var integer
73861 */
73862 private $exitCode;
73863
73864 public function __construct(Command $command, InputInterface $input, OutputInterface $output, $exitCode)
73865 {
73866 parent::__construct($command, $input, $output);
73867
73868 $this->setExitCode($exitCode);
73869 }
73870
73871 /**
73872 * Sets the exit code.
73873 *
73874 * @param integer $exitCode The command exit code
73875 */
73876 public function setExitCode($exitCode)
73877 {
73878 $this->exitCode = $exitCode;
73879 }
73880
73881 /**
73882 * Gets the exit code.
73883 *
73884 * @return integer The command exit code
73885 */
73886 public function getExitCode()
73887 {
73888 return $this->exitCode;
73889 }
73890 }
73891 <?php
73892
73893 /*
73894 * This file is part of the Symfony package.
73895 *
73896 * (c) Fabien Potencier <fabien@symfony.com>
73897 *
73898 * For the full copyright and license information, please view the LICENSE
73899 * file that was distributed with this source code.
73900 */
73901
73902 namespace Symfony\Component\Console\Event;
73903
73904 use Symfony\Component\Console\Command\Command;
73905 use Symfony\Component\Console\Input\InputInterface;
73906 use Symfony\Component\Console\Output\OutputInterface;
73907
73908 /**
73909 * Allows to handle exception thrown in a command.
73910 *
73911 * @author Fabien Potencier <fabien@symfony.com>
73912 */
73913 class ConsoleExceptionEvent extends ConsoleEvent
73914 {
73915 private $exception;
73916 private $exitCode;
73917
73918 public function __construct(Command $command, InputInterface $input, OutputInterface $output, \Exception $exception, $exitCode)
73919 {
73920 parent::__construct($command, $input, $output);
73921
73922 $this->setException($exception);
73923 $this->exitCode = $exitCode;
73924 }
73925
73926 /**
73927 * Returns the thrown exception.
73928 *
73929 * @return \Exception The thrown exception
73930 */
73931 public function getException()
73932 {
73933 return $this->exception;
73934 }
73935
73936 /**
73937 * Replaces the thrown exception.
73938 *
73939 * This exception will be thrown if no response is set in the event.
73940 *
73941 * @param \Exception $exception The thrown exception
73942 */
73943 public function setException(\Exception $exception)
73944 {
73945 $this->exception = $exception;
73946 }
73947
73948 /**
73949 * Gets the exit code.
73950 *
73951 * @return integer The command exit code
73952 */
73953 public function getExitCode()
73954 {
73955 return $this->exitCode;
73956 }
73957 }
73958 <?php
73959
73960 /*
73961 * This file is part of the Symfony package.
73962 *
73963 * (c) Fabien Potencier <fabien@symfony.com>
73964 *
73965 * For the full copyright and license information, please view the LICENSE
73966 * file that was distributed with this source code.
73967 */
73968
73969 namespace Symfony\Component\Console\Event;
73970
73971 use Symfony\Component\Console\Command\Command;
73972 use Symfony\Component\Console\Input\InputInterface;
73973 use Symfony\Component\Console\Output\OutputInterface;
73974 use Symfony\Component\EventDispatcher\Event;
73975
73976 /**
73977 * Allows to inspect input and output of a command.
73978 *
73979 * @author Francesco Levorato <git@flevour.net>
73980 */
73981 class ConsoleEvent extends Event
73982 {
73983 protected $command;
73984
73985 private $input;
73986 private $output;
73987
73988 public function __construct(Command $command, InputInterface $input, OutputInterface $output)
73989 {
73990 $this->command = $command;
73991 $this->input = $input;
73992 $this->output = $output;
73993 }
73994
73995 /**
73996 * Gets the command that is executed.
73997 *
73998 * @return Command A Command instance
73999 */
74000 public function getCommand()
74001 {
74002 return $this->command;
74003 }
74004
74005 /**
74006 * Gets the input instance.
74007 *
74008 * @return InputInterface An InputInterface instance
74009 */
74010 public function getInput()
74011 {
74012 return $this->input;
74013 }
74014
74015 /**
74016 * Gets the output instance.
74017 *
74018 * @return OutputInterface An OutputInterface instance
74019 */
74020 public function getOutput()
74021 {
74022 return $this->output;
74023 }
74024 }
74025 <?php
74026
74027 /*
74028 * This file is part of the Symfony package.
74029 *
74030 * (c) Fabien Potencier <fabien@symfony.com>
74031 *
74032 * For the full copyright and license information, please view the LICENSE
74033 * file that was distributed with this source code.
74034 */
74035
74036 namespace Symfony\Component\Console\Event;
74037
74038 use Symfony\Component\Console\Command\Command;
74039
74040 /**
74041 * Allows to do things before the command is executed.
74042 *
74043 * @author Fabien Potencier <fabien@symfony.com>
74044 */
74045 class ConsoleCommandEvent extends ConsoleEvent
74046 {
74047 }
74048 <?php
74049
74050 /*
74051 * This file is part of the Symfony package.
74052 *
74053 * (c) Fabien Potencier <fabien@symfony.com>
74054 *
74055 * For the full copyright and license information, please view the LICENSE
74056 * file that was distributed with this source code.
74057 */
74058
74059 namespace Symfony\Component\Console\Helper;
74060
74061 use Symfony\Component\Console\Output\OutputInterface;
74062 use InvalidArgumentException;
74063
74064 /**
74065 * Provides helpers to display table output.
74066 *
74067 * @author Саша Стаменковић <umpirsky@gmail.com>
74068 */
74069 class TableHelper extends Helper
74070 {
74071 const LAYOUT_DEFAULT = 0;
74072 const LAYOUT_BORDERLESS = 1;
74073
74074 /**
74075 * Table headers.
74076 *
74077 * @var array
74078 */
74079 private $headers = array();
74080
74081 /**
74082 * Table rows.
74083 *
74084 * @var array
74085 */
74086 private $rows = array();
74087
74088 // Rendering options
74089 private $paddingChar;
74090 private $horizontalBorderChar;
74091 private $verticalBorderChar;
74092 private $crossingChar;
74093 private $cellHeaderFormat;
74094 private $cellRowFormat;
74095 private $borderFormat;
74096 private $padType;
74097
74098 /**
74099 * Column widths cache.
74100 *
74101 * @var array
74102 */
74103 private $columnWidths = array();
74104
74105 /**
74106 * Number of columns cache.
74107 *
74108 * @var array
74109 */
74110 private $numberOfColumns;
74111
74112 /**
74113 * @var OutputInterface
74114 */
74115 private $output;
74116
74117 public function __construct()
74118 {
74119 $this->setLayout(self::LAYOUT_DEFAULT);
74120 }
74121
74122 /**
74123 * Sets table layout type.
74124 *
74125 * @param int $layout self::LAYOUT_*
74126 *
74127 * @return TableHelper
74128 */
74129 public function setLayout($layout)
74130 {
74131 switch ($layout) {
74132 case self::LAYOUT_BORDERLESS:
74133 $this
74134 ->setPaddingChar(' ')
74135 ->setHorizontalBorderChar('=')
74136 ->setVerticalBorderChar(' ')
74137 ->setCrossingChar(' ')
74138 ->setCellHeaderFormat('<info>%s</info>')
74139 ->setCellRowFormat('<comment>%s</comment>')
74140 ->setBorderFormat('%s')
74141 ->setPadType(STR_PAD_RIGHT)
74142 ;
74143 break;
74144
74145 case self::LAYOUT_DEFAULT:
74146 $this
74147 ->setPaddingChar(' ')
74148 ->setHorizontalBorderChar('-')
74149 ->setVerticalBorderChar('|')
74150 ->setCrossingChar('+')
74151 ->setCellHeaderFormat('<info>%s</info>')
74152 ->setCellRowFormat('<comment>%s</comment>')
74153 ->setBorderFormat('%s')
74154 ->setPadType(STR_PAD_RIGHT)
74155 ;
74156 break;
74157
74158 default:
74159 throw new InvalidArgumentException(sprintf('Invalid table layout "%s".', $layout));
74160 break;
74161 };
74162
74163 return $this;
74164 }
74165
74166 public function setHeaders(array $headers)
74167 {
74168 $this->headers = array_values($headers);
74169
74170 return $this;
74171 }
74172
74173 public function setRows(array $rows)
74174 {
74175 $this->rows = array();
74176
74177 return $this->addRows($rows);
74178 }
74179
74180 public function addRows(array $rows)
74181 {
74182 foreach ($rows as $row) {
74183 $this->addRow($row);
74184 }
74185
74186 return $this;
74187 }
74188
74189 public function addRow(array $row)
74190 {
74191 $this->rows[] = array_values($row);
74192
74193 return $this;
74194 }
74195
74196 public function setRow($column, array $row)
74197 {
74198 $this->rows[$column] = $row;
74199
74200 return $this;
74201 }
74202
74203 /**
74204 * Sets padding character, used for cell padding.
74205 *
74206 * @param string $paddingChar
74207 *
74208 * @return TableHelper
74209 */
74210 public function setPaddingChar($paddingChar)
74211 {
74212 $this->paddingChar = $paddingChar;
74213
74214 return $this;
74215 }
74216
74217 /**
74218 * Sets horizontal border character.
74219 *
74220 * @param string $horizontalBorderChar
74221 *
74222 * @return TableHelper
74223 */
74224 public function setHorizontalBorderChar($horizontalBorderChar)
74225 {
74226 $this->horizontalBorderChar = $horizontalBorderChar;
74227
74228 return $this;
74229 }
74230
74231 /**
74232 * Sets vertical border character.
74233 *
74234 * @param string $verticalBorderChar
74235 *
74236 * @return TableHelper
74237 */
74238 public function setVerticalBorderChar($verticalBorderChar)
74239 {
74240 $this->verticalBorderChar = $verticalBorderChar;
74241
74242 return $this;
74243 }
74244
74245 /**
74246 * Sets crossing character.
74247 *
74248 * @param string $crossingChar
74249 *
74250 * @return TableHelper
74251 */
74252 public function setCrossingChar($crossingChar)
74253 {
74254 $this->crossingChar = $crossingChar;
74255
74256 return $this;
74257 }
74258
74259 /**
74260 * Sets header cell format.
74261 *
74262 * @param string $cellHeaderFormat
74263 *
74264 * @return TableHelper
74265 */
74266 public function setCellHeaderFormat($cellHeaderFormat)
74267 {
74268 $this->cellHeaderFormat = $cellHeaderFormat;
74269
74270 return $this;
74271 }
74272
74273 /**
74274 * Sets row cell format.
74275 *
74276 * @param string $cellRowFormat
74277 *
74278 * @return TableHelper
74279 */
74280 public function setCellRowFormat($cellRowFormat)
74281 {
74282 $this->cellRowFormat = $cellRowFormat;
74283
74284 return $this;
74285 }
74286
74287 /**
74288 * Sets table border format.
74289 *
74290 * @param string $borderFormat
74291 *
74292 * @return TableHelper
74293 */
74294 public function setBorderFormat($borderFormat)
74295 {
74296 $this->borderFormat = $borderFormat;
74297
74298 return $this;
74299 }
74300
74301 /**
74302 * Sets cell padding type.
74303 *
74304 * @param integer $padType STR_PAD_*
74305 *
74306 * @return TableHelper
74307 */
74308 public function setPadType($padType)
74309 {
74310 $this->padType = $padType;
74311
74312 return $this;
74313 }
74314
74315 /**
74316 * Renders table to output.
74317 *
74318 * Example:
74319 * +---------------+-----------------------+------------------+
74320 * | ISBN | Title | Author |
74321 * +---------------+-----------------------+------------------+
74322 * | 99921-58-10-7 | Divine Comedy | Dante Alighieri |
74323 * | 9971-5-0210-0 | A Tale of Two Cities | Charles Dickens |
74324 * | 960-425-059-0 | The Lord of the Rings | J. R. R. Tolkien |
74325 * +---------------+-----------------------+------------------+
74326 *
74327 * @param OutputInterface $output
74328 */
74329 public function render(OutputInterface $output)
74330 {
74331 $this->output = $output;
74332
74333 $this->renderRowSeparator();
74334 $this->renderRow($this->headers, $this->cellHeaderFormat);
74335 if (!empty($this->headers)) {
74336 $this->renderRowSeparator();
74337 }
74338 foreach ($this->rows as $row) {
74339 $this->renderRow($row, $this->cellRowFormat);
74340 }
74341 if (!empty($this->rows)) {
74342 $this->renderRowSeparator();
74343 }
74344
74345 $this->cleanup();
74346 }
74347
74348 /**
74349 * Renders horizontal header separator.
74350 *
74351 * Example: +-----+-----------+-------+
74352 */
74353 private function renderRowSeparator()
74354 {
74355 if (0 === $count = $this->getNumberOfColumns()) {
74356 return;
74357 }
74358
74359 $markup = $this->crossingChar;
74360 for ($column = 0; $column < $count; $column++) {
74361 $markup .= str_repeat($this->horizontalBorderChar, $this->getColumnWidth($column))
74362 .$this->crossingChar
74363 ;
74364 }
74365
74366 $this->output->writeln(sprintf($this->borderFormat, $markup));
74367 }
74368
74369 /**
74370 * Renders vertical column separator.
74371 */
74372 private function renderColumnSeparator()
74373 {
74374 $this->output->write(sprintf($this->borderFormat, $this->verticalBorderChar));
74375 }
74376
74377 /**
74378 * Renders table row.
74379 *
74380 * Example: | 9971-5-0210-0 | A Tale of Two Cities | Charles Dickens |
74381 *
74382 * @param array $row
74383 * @param string $cellFormat
74384 */
74385 private function renderRow(array $row, $cellFormat)
74386 {
74387 if (empty($row)) {
74388 return;
74389 }
74390
74391 $this->renderColumnSeparator();
74392 for ($column = 0, $count = $this->getNumberOfColumns(); $column < $count; $column++) {
74393 $this->renderCell($row, $column, $cellFormat);
74394 $this->renderColumnSeparator();
74395 }
74396 $this->output->writeln('');
74397 }
74398
74399 /**
74400 * Renders table cell with padding.
74401 *
74402 * @param array $row
74403 * @param integer $column
74404 * @param string $cellFormat
74405 */
74406 private function renderCell(array $row, $column, $cellFormat)
74407 {
74408 $cell = isset($row[$column]) ? $row[$column] : '';
74409 $width = $this->getColumnWidth($column);
74410
74411 // str_pad won't work properly with multi-byte strings, we need to fix the padding
74412 if (function_exists('mb_strlen') && false !== $encoding = mb_detect_encoding($cell)) {
74413 $width += strlen($cell) - mb_strlen($cell, $encoding);
74414 }
74415
74416 $this->output->write(sprintf(
74417 $cellFormat,
74418 str_pad(
74419 $this->paddingChar.$cell.$this->paddingChar,
74420 $width,
74421 $this->paddingChar,
74422 $this->padType
74423 )
74424 ));
74425 }
74426
74427 /**
74428 * Gets number of columns for this table.
74429 *
74430 * @return int
74431 */
74432 private function getNumberOfColumns()
74433 {
74434 if (null !== $this->numberOfColumns) {
74435 return $this->numberOfColumns;
74436 }
74437
74438 $columns = array(0);
74439 $columns[] = count($this->headers);
74440 foreach ($this->rows as $row) {
74441 $columns[] = count($row);
74442 }
74443
74444 return $this->numberOfColumns = max($columns);
74445 }
74446
74447 /**
74448 * Gets column width.
74449 *
74450 * @param integer $column
74451 *
74452 * @return int
74453 */
74454 private function getColumnWidth($column)
74455 {
74456 if (isset($this->columnWidths[$column])) {
74457 return $this->columnWidths[$column];
74458 }
74459
74460 $lengths = array(0);
74461 $lengths[] = $this->getCellWidth($this->headers, $column);
74462 foreach ($this->rows as $row) {
74463 $lengths[] = $this->getCellWidth($row, $column);
74464 }
74465
74466 return $this->columnWidths[$column] = max($lengths) + 2;
74467 }
74468
74469 /**
74470 * Gets cell width.
74471 *
74472 * @param array $row
74473 * @param integer $column
74474 *
74475 * @return int
74476 */
74477 private function getCellWidth(array $row, $column)
74478 {
74479 if ($column < 0) {
74480 return 0;
74481 }
74482
74483 if (isset($row[$column])) {
74484 return $this->strlen($row[$column]);
74485 }
74486
74487 return $this->getCellWidth($row, $column - 1);
74488 }
74489
74490 /**
74491 * Called after rendering to cleanup cache data.
74492 */
74493 private function cleanup()
74494 {
74495 $this->columnWidths = array();
74496 $this->numberOfColumns = null;
74497 }
74498
74499 /**
74500 * {@inheritDoc}
74501 */
74502 public function getName()
74503 {
74504 return 'table';
74505 }
74506 }
74507 <?php
74508
74509 /*
74510 * This file is part of the Symfony package.
74511 *
74512 * (c) Fabien Potencier <fabien@symfony.com>
74513 *
74514 * For the full copyright and license information, please view the LICENSE
74515 * file that was distributed with this source code.
74516 */
74517
74518 namespace Symfony\Component\Console\Helper;
74519
74520 use Symfony\Component\Console\Output\OutputInterface;
74521
74522 /**
74523 * The Progress class provides helpers to display progress output.
74524 *
74525 * @author Chris Jones <leeked@gmail.com>
74526 * @author Fabien Potencier <fabien@symfony.com>
74527 */
74528 class ProgressHelper extends Helper
74529 {
74530 const FORMAT_QUIET = ' %percent%%';
74531 const FORMAT_NORMAL = ' %current%/%max% [%bar%] %percent%%';
74532 const FORMAT_VERBOSE = ' %current%/%max% [%bar%] %percent%% Elapsed: %elapsed%';
74533 const FORMAT_QUIET_NOMAX = ' %current%';
74534 const FORMAT_NORMAL_NOMAX = ' %current% [%bar%]';
74535 const FORMAT_VERBOSE_NOMAX = ' %current% [%bar%] Elapsed: %elapsed%';
74536
74537 // options
74538 private $barWidth = 28;
74539 private $barChar = '=';
74540 private $emptyBarChar = '-';
74541 private $progressChar = '>';
74542 private $format = null;
74543 private $redrawFreq = 1;
74544
74545 private $lastMessagesLength;
74546 private $barCharOriginal;
74547
74548 /**
74549 * @var OutputInterface
74550 */
74551 private $output;
74552
74553 /**
74554 * Current step
74555 *
74556 * @var integer
74557 */
74558 private $current;
74559
74560 /**
74561 * Maximum number of steps
74562 *
74563 * @var integer
74564 */
74565 private $max;
74566
74567 /**
74568 * Start time of the progress bar
74569 *
74570 * @var integer
74571 */
74572 private $startTime;
74573
74574 /**
74575 * List of formatting variables
74576 *
74577 * @var array
74578 */
74579 private $defaultFormatVars = array(
74580 'current',
74581 'max',
74582 'bar',
74583 'percent',
74584 'elapsed',
74585 );
74586
74587 /**
74588 * Available formatting variables
74589 *
74590 * @var array
74591 */
74592 private $formatVars;
74593
74594 /**
74595 * Stored format part widths (used for padding)
74596 *
74597 * @var array
74598 */
74599 private $widths = array(
74600 'current' => 4,
74601 'max' => 4,
74602 'percent' => 3,
74603 'elapsed' => 6,
74604 );
74605
74606 /**
74607 * Various time formats
74608 *
74609 * @var array
74610 */
74611 private $timeFormats = array(
74612 array(0, '???'),
74613 array(2, '1 sec'),
74614 array(59, 'secs', 1),
74615 array(60, '1 min'),
74616 array(3600, 'mins', 60),
74617 array(5400, '1 hr'),
74618 array(86400, 'hrs', 3600),
74619 array(129600, '1 day'),
74620 array(604800, 'days', 86400),
74621 );
74622
74623 /**
74624 * Sets the progress bar width.
74625 *
74626 * @param int $size The progress bar size
74627 */
74628 public function setBarWidth($size)
74629 {
74630 $this->barWidth = (int) $size;
74631 }
74632
74633 /**
74634 * Sets the bar character.
74635 *
74636 * @param string $char A character
74637 */
74638 public function setBarCharacter($char)
74639 {
74640 $this->barChar = $char;
74641 }
74642
74643 /**
74644 * Sets the empty bar character.
74645 *
74646 * @param string $char A character
74647 */
74648 public function setEmptyBarCharacter($char)
74649 {
74650 $this->emptyBarChar = $char;
74651 }
74652
74653 /**
74654 * Sets the progress bar character.
74655 *
74656 * @param string $char A character
74657 */
74658 public function setProgressCharacter($char)
74659 {
74660 $this->progressChar = $char;
74661 }
74662
74663 /**
74664 * Sets the progress bar format.
74665 *
74666 * @param string $format The format
74667 */
74668 public function setFormat($format)
74669 {
74670 $this->format = $format;
74671 }
74672
74673 /**
74674 * Sets the redraw frequency.
74675 *
74676 * @param int $freq The frequency in steps
74677 */
74678 public function setRedrawFrequency($freq)
74679 {
74680 $this->redrawFreq = (int) $freq;
74681 }
74682
74683 /**
74684 * Starts the progress output.
74685 *
74686 * @param OutputInterface $output An Output instance
74687 * @param integer $max Maximum steps
74688 */
74689 public function start(OutputInterface $output, $max = null)
74690 {
74691 $this->startTime = time();
74692 $this->current = 0;
74693 $this->max = (int) $max;
74694 $this->output = $output;
74695 $this->lastMessagesLength = 0;
74696 $this->barCharOriginal = '';
74697
74698 if (null === $this->format) {
74699 switch ($output->getVerbosity()) {
74700 case OutputInterface::VERBOSITY_QUIET:
74701 $this->format = self::FORMAT_QUIET_NOMAX;
74702 if ($this->max > 0) {
74703 $this->format = self::FORMAT_QUIET;
74704 }
74705 break;
74706 case OutputInterface::VERBOSITY_VERBOSE:
74707 case OutputInterface::VERBOSITY_VERY_VERBOSE:
74708 case OutputInterface::VERBOSITY_DEBUG:
74709 $this->format = self::FORMAT_VERBOSE_NOMAX;
74710 if ($this->max > 0) {
74711 $this->format = self::FORMAT_VERBOSE;
74712 }
74713 break;
74714 default:
74715 $this->format = self::FORMAT_NORMAL_NOMAX;
74716 if ($this->max > 0) {
74717 $this->format = self::FORMAT_NORMAL;
74718 }
74719 break;
74720 }
74721 }
74722
74723 $this->initialize();
74724 }
74725
74726 /**
74727 * Advances the progress output X steps.
74728 *
74729 * @param integer $step Number of steps to advance
74730 * @param Boolean $redraw Whether to redraw or not
74731 *
74732 * @throws \LogicException
74733 */
74734 public function advance($step = 1, $redraw = false)
74735 {
74736 if (null === $this->startTime) {
74737 throw new \LogicException('You must start the progress bar before calling advance().');
74738 }
74739
74740 if (0 === $this->current) {
74741 $redraw = true;
74742 }
74743
74744 $this->current += $step;
74745 if ($redraw || 0 === $this->current % $this->redrawFreq) {
74746 $this->display();
74747 }
74748 }
74749
74750 /**
74751 * Sets the current progress.
74752 *
74753 * @param integer $current The current progress
74754 * @param Boolean $redraw Whether to redraw or not
74755 *
74756 * @throws \LogicException
74757 */
74758 public function setCurrent($current, $redraw = false)
74759 {
74760 if (null === $this->startTime) {
74761 throw new \LogicException('You must start the progress bar before calling setCurrent().');
74762 }
74763
74764 $current = (int) $current;
74765
74766 if ($current < $this->current) {
74767 throw new \LogicException('You can\'t regress the progress bar');
74768 }
74769
74770 if (0 === $this->current) {
74771 $redraw = true;
74772 }
74773
74774 $this->current = $current;
74775 if ($redraw || 0 === $this->current % $this->redrawFreq) {
74776 $this->display();
74777 }
74778 }
74779
74780 /**
74781 * Outputs the current progress string.
74782 *
74783 * @param Boolean $finish Forces the end result
74784 *
74785 * @throws \LogicException
74786 */
74787 public function display($finish = false)
74788 {
74789 if (null === $this->startTime) {
74790 throw new \LogicException('You must start the progress bar before calling display().');
74791 }
74792
74793 $message = $this->format;
74794 foreach ($this->generate($finish) as $name => $value) {
74795 $message = str_replace("%{$name}%", $value, $message);
74796 }
74797 $this->overwrite($this->output, $message);
74798 }
74799
74800 /**
74801 * Finishes the progress output.
74802 */
74803 public function finish()
74804 {
74805 if (null === $this->startTime) {
74806 throw new \LogicException('You must start the progress bar before calling finish().');
74807 }
74808
74809 if (null !== $this->startTime) {
74810 if (!$this->max) {
74811 $this->barChar = $this->barCharOriginal;
74812 $this->display(true);
74813 }
74814 $this->startTime = null;
74815 $this->output->writeln('');
74816 $this->output = null;
74817 }
74818 }
74819
74820 /**
74821 * Initializes the progress helper.
74822 */
74823 private function initialize()
74824 {
74825 $this->formatVars = array();
74826 foreach ($this->defaultFormatVars as $var) {
74827 if (false !== strpos($this->format, "%{$var}%")) {
74828 $this->formatVars[$var] = true;
74829 }
74830 }
74831
74832 if ($this->max > 0) {
74833 $this->widths['max'] = $this->strlen($this->max);
74834 $this->widths['current'] = $this->widths['max'];
74835 } else {
74836 $this->barCharOriginal = $this->barChar;
74837 $this->barChar = $this->emptyBarChar;
74838 }
74839 }
74840
74841 /**
74842 * Generates the array map of format variables to values.
74843 *
74844 * @param Boolean $finish Forces the end result
74845 *
74846 * @return array Array of format vars and values
74847 */
74848 private function generate($finish = false)
74849 {
74850 $vars = array();
74851 $percent = 0;
74852 if ($this->max > 0) {
74853 $percent = (double) $this->current / $this->max;
74854 }
74855
74856 if (isset($this->formatVars['bar'])) {
74857 $completeBars = 0;
74858
74859 if ($this->max > 0) {
74860 $completeBars = floor($percent * $this->barWidth);
74861 } else {
74862 if (!$finish) {
74863 $completeBars = floor($this->current % $this->barWidth);
74864 } else {
74865 $completeBars = $this->barWidth;
74866 }
74867 }
74868
74869 $emptyBars = $this->barWidth - $completeBars - $this->strlen($this->progressChar);
74870 $bar = str_repeat($this->barChar, $completeBars);
74871 if ($completeBars < $this->barWidth) {
74872 $bar .= $this->progressChar;
74873 $bar .= str_repeat($this->emptyBarChar, $emptyBars);
74874 }
74875
74876 $vars['bar'] = $bar;
74877 }
74878
74879 if (isset($this->formatVars['elapsed'])) {
74880 $elapsed = time() - $this->startTime;
74881 $vars['elapsed'] = str_pad($this->humaneTime($elapsed), $this->widths['elapsed'], ' ', STR_PAD_LEFT);
74882 }
74883
74884 if (isset($this->formatVars['current'])) {
74885 $vars['current'] = str_pad($this->current, $this->widths['current'], ' ', STR_PAD_LEFT);
74886 }
74887
74888 if (isset($this->formatVars['max'])) {
74889 $vars['max'] = $this->max;
74890 }
74891
74892 if (isset($this->formatVars['percent'])) {
74893 $vars['percent'] = str_pad(floor($percent * 100), $this->widths['percent'], ' ', STR_PAD_LEFT);
74894 }
74895
74896 return $vars;
74897 }
74898
74899 /**
74900 * Converts seconds into human-readable format.
74901 *
74902 * @param integer $secs Number of seconds
74903 *
74904 * @return string Time in readable format
74905 */
74906 private function humaneTime($secs)
74907 {
74908 $text = '';
74909 foreach ($this->timeFormats as $format) {
74910 if ($secs < $format[0]) {
74911 if (count($format) == 2) {
74912 $text = $format[1];
74913 break;
74914 } else {
74915 $text = ceil($secs / $format[2]).' '.$format[1];
74916 break;
74917 }
74918 }
74919 }
74920
74921 return $text;
74922 }
74923
74924 /**
74925 * Overwrites a previous message to the output.
74926 *
74927 * @param OutputInterface $output An Output instance
74928 * @param string $message The message
74929 */
74930 private function overwrite(OutputInterface $output, $message)
74931 {
74932 $length = $this->strlen($message);
74933
74934 // append whitespace to match the last line's length
74935 if (null !== $this->lastMessagesLength && $this->lastMessagesLength > $length) {
74936 $message = str_pad($message, $this->lastMessagesLength, "\x20", STR_PAD_RIGHT);
74937 }
74938
74939 // carriage return
74940 $output->write("\x0D");
74941 $output->write($message);
74942
74943 $this->lastMessagesLength = $this->strlen($message);
74944 }
74945
74946 /**
74947 * {@inheritDoc}
74948 */
74949 public function getName()
74950 {
74951 return 'progress';
74952 }
74953 }
74954 <?php
74955
74956 /*
74957 * This file is part of the Symfony package.
74958 *
74959 * (c) Fabien Potencier <fabien@symfony.com>
74960 *
74961 * For the full copyright and license information, please view the LICENSE
74962 * file that was distributed with this source code.
74963 */
74964
74965 namespace Symfony\Component\Console\Helper;
74966
74967 /**
74968 * Helper is the base class for all helper classes.
74969 *
74970 * @author Fabien Potencier <fabien@symfony.com>
74971 */
74972 abstract class Helper implements HelperInterface
74973 {
74974 protected $helperSet = null;
74975
74976 /**
74977 * Sets the helper set associated with this helper.
74978 *
74979 * @param HelperSet $helperSet A HelperSet instance
74980 */
74981 public function setHelperSet(HelperSet $helperSet = null)
74982 {
74983 $this->helperSet = $helperSet;
74984 }
74985
74986 /**
74987 * Gets the helper set associated with this helper.
74988 *
74989 * @return HelperSet A HelperSet instance
74990 */
74991 public function getHelperSet()
74992 {
74993 return $this->helperSet;
74994 }
74995
74996 /**
74997 * Returns the length of a string, using mb_strlen if it is available.
74998 *
74999 * @param string $string The string to check its length
75000 *
75001 * @return integer The length of the string
75002 */
75003 protected function strlen($string)
75004 {
75005 if (!function_exists('mb_strlen')) {
75006 return strlen($string);
75007 }
75008
75009 if (false === $encoding = mb_detect_encoding($string)) {
75010 return strlen($string);
75011 }
75012
75013 return mb_strlen($string, $encoding);
75014 }
75015 }
75016 <?php
75017
75018 /*
75019 * This file is part of the Symfony package.
75020 *
75021 * (c) Fabien Potencier <fabien@symfony.com>
75022 *
75023 * For the full copyright and license information, please view the LICENSE
75024 * file that was distributed with this source code.
75025 */
75026
75027 namespace Symfony\Component\Console\Helper;
75028
75029 use Symfony\Component\Console\Descriptor\DescriptorInterface;
75030 use Symfony\Component\Console\Descriptor\JsonDescriptor;
75031 use Symfony\Component\Console\Descriptor\MarkdownDescriptor;
75032 use Symfony\Component\Console\Descriptor\TextDescriptor;
75033 use Symfony\Component\Console\Descriptor\XmlDescriptor;
75034 use Symfony\Component\Console\Output\OutputInterface;
75035
75036 /**
75037 * This class adds helper method to describe objects in various formats.
75038 *
75039 * @author Jean-François Simon <contact@jfsimon.fr>
75040 */
75041 class DescriptorHelper extends Helper
75042 {
75043 /**
75044 * @var DescriptorInterface[]
75045 */
75046 private $descriptors = array();
75047
75048 /**
75049 * Constructor.
75050 */
75051 public function __construct()
75052 {
75053 $this
75054 ->register('txt', new TextDescriptor())
75055 ->register('xml', new XmlDescriptor())
75056 ->register('json', new JsonDescriptor())
75057 ->register('md', new MarkdownDescriptor())
75058 ;
75059 }
75060
75061 /**
75062 * Describes an object if supported.
75063 *
75064 * @param OutputInterface $output
75065 * @param object $object
75066 * @param string $format
75067 * @param boolean $raw
75068 */
75069 public function describe(OutputInterface $output, $object, $format = null, $raw = false, $namespace = null)
75070 {
75071 $options = array('raw_text' => $raw, 'format' => $format ?: 'txt', 'namespace' => $namespace);
75072 $type = !$raw && 'txt' === $options['format'] ? OutputInterface::OUTPUT_NORMAL : OutputInterface::OUTPUT_RAW;
75073
75074 if (!isset($this->descriptors[$options['format']])) {
75075 throw new \InvalidArgumentException(sprintf('Unsupported format "%s".', $options['format']));
75076 }
75077
75078 $descriptor = $this->descriptors[$options['format']];
75079
75080 $output->writeln($descriptor->describe($object, $options), $type);
75081 }
75082
75083 /**
75084 * Registers a descriptor.
75085 *
75086 * @param string $format
75087 * @param DescriptorInterface $descriptor
75088 *
75089 * @return DescriptorHelper
75090 */
75091 public function register($format, DescriptorInterface $descriptor)
75092 {
75093 $this->descriptors[$format] = $descriptor;
75094
75095 return $this;
75096 }
75097
75098 /**
75099 * {@inheritdoc}
75100 */
75101 public function getName()
75102 {
75103 return 'descriptor';
75104 }
75105 }
75106 <?php
75107
75108 /*
75109 * This file is part of the Symfony package.
75110 *
75111 * (c) Fabien Potencier <fabien@symfony.com>
75112 *
75113 * For the full copyright and license information, please view the LICENSE
75114 * file that was distributed with this source code.
75115 */
75116
75117 namespace Symfony\Component\Console\Helper;
75118
75119 use Symfony\Component\Console\Command\Command;
75120
75121 /**
75122 * HelperSet represents a set of helpers to be used with a command.
75123 *
75124 * @author Fabien Potencier <fabien@symfony.com>
75125 */
75126 class HelperSet
75127 {
75128 private $helpers;
75129 private $command;
75130
75131 /**
75132 * Constructor.
75133 *
75134 * @param Helper[] $helpers An array of helper.
75135 */
75136 public function __construct(array $helpers = array())
75137 {
75138 $this->helpers = array();
75139 foreach ($helpers as $alias => $helper) {
75140 $this->set($helper, is_int($alias) ? null : $alias);
75141 }
75142 }
75143
75144 /**
75145 * Sets a helper.
75146 *
75147 * @param HelperInterface $helper The helper instance
75148 * @param string $alias An alias
75149 */
75150 public function set(HelperInterface $helper, $alias = null)
75151 {
75152 $this->helpers[$helper->getName()] = $helper;
75153 if (null !== $alias) {
75154 $this->helpers[$alias] = $helper;
75155 }
75156
75157 $helper->setHelperSet($this);
75158 }
75159
75160 /**
75161 * Returns true if the helper if defined.
75162 *
75163 * @param string $name The helper name
75164 *
75165 * @return Boolean true if the helper is defined, false otherwise
75166 */
75167 public function has($name)
75168 {
75169 return isset($this->helpers[$name]);
75170 }
75171
75172 /**
75173 * Gets a helper value.
75174 *
75175 * @param string $name The helper name
75176 *
75177 * @return HelperInterface The helper instance
75178 *
75179 * @throws \InvalidArgumentException if the helper is not defined
75180 */
75181 public function get($name)
75182 {
75183 if (!$this->has($name)) {
75184 throw new \InvalidArgumentException(sprintf('The helper "%s" is not defined.', $name));
75185 }
75186
75187 return $this->helpers[$name];
75188 }
75189
75190 /**
75191 * Sets the command associated with this helper set.
75192 *
75193 * @param Command $command A Command instance
75194 */
75195 public function setCommand(Command $command = null)
75196 {
75197 $this->command = $command;
75198 }
75199
75200 /**
75201 * Gets the command associated with this helper set.
75202 *
75203 * @return Command A Command instance
75204 */
75205 public function getCommand()
75206 {
75207 return $this->command;
75208 }
75209 }
75210 <?php
75211
75212 /*
75213 * This file is part of the Symfony package.
75214 *
75215 * (c) Fabien Potencier <fabien@symfony.com>
75216 *
75217 * For the full copyright and license information, please view the LICENSE
75218 * file that was distributed with this source code.
75219 */
75220
75221 namespace Symfony\Component\Console\Helper;
75222
75223 use Symfony\Component\Console\Output\OutputInterface;
75224 use Symfony\Component\Console\Formatter\OutputFormatterStyle;
75225
75226 /**
75227 * The Dialog class provides helpers to interact with the user.
75228 *
75229 * @author Fabien Potencier <fabien@symfony.com>
75230 */
75231 class DialogHelper extends Helper
75232 {
75233 private $inputStream;
75234 private static $shell;
75235 private static $stty;
75236
75237 /**
75238 * Asks the user to select a value.
75239 *
75240 * @param OutputInterface $output An Output instance
75241 * @param string|array $question The question to ask
75242 * @param array $choices List of choices to pick from
75243 * @param Boolean $default The default answer if the user enters nothing
75244 * @param Boolean|integer $attempts Max number of times to ask before giving up (false by default, which means infinite)
75245 * @param string $errorMessage Message which will be shown if invalid value from choice list would be picked
75246 * @param Boolean $multiselect Select more than one value separated by comma
75247 *
75248 * @return integer|string|array The selected value or values (the key of the choices array)
75249 *
75250 * @throws \InvalidArgumentException
75251 */
75252 public function select(OutputInterface $output, $question, $choices, $default = null, $attempts = false, $errorMessage = 'Value "%s" is invalid', $multiselect = false)
75253 {
75254 $width = max(array_map('strlen', array_keys($choices)));
75255
75256 $messages = (array) $question;
75257 foreach ($choices as $key => $value) {
75258 $messages[] = sprintf(" [<info>%-${width}s</info>] %s", $key, $value);
75259 }
75260
75261 $output->writeln($messages);
75262
75263 $result = $this->askAndValidate($output, '> ', function ($picked) use ($choices, $errorMessage, $multiselect) {
75264 // Collapse all spaces.
75265 $selectedChoices = str_replace(" ", "", $picked);
75266
75267 if ($multiselect) {
75268 // Check for a separated comma values
75269 if (!preg_match('/^[a-zA-Z0-9_-]+(?:,[a-zA-Z0-9_-]+)*$/', $selectedChoices, $matches)) {
75270 throw new \InvalidArgumentException(sprintf($errorMessage, $picked));
75271 }
75272 $selectedChoices = explode(",", $selectedChoices);
75273 } else {
75274 $selectedChoices = array($picked);
75275 }
75276
75277 $multiselectChoices = array();
75278
75279 foreach ($selectedChoices as $value) {
75280 if (empty($choices[$value])) {
75281 throw new \InvalidArgumentException(sprintf($errorMessage, $value));
75282 }
75283 array_push($multiselectChoices, $value);
75284 }
75285
75286 if ($multiselect) {
75287 return $multiselectChoices;
75288 }
75289
75290 return $picked;
75291 }, $attempts, $default);
75292
75293 return $result;
75294 }
75295
75296 /**
75297 * Asks a question to the user.
75298 *
75299 * @param OutputInterface $output An Output instance
75300 * @param string|array $question The question to ask
75301 * @param string $default The default answer if none is given by the user
75302 * @param array $autocomplete List of values to autocomplete
75303 *
75304 * @return string The user answer
75305 *
75306 * @throws \RuntimeException If there is no data to read in the input stream
75307 */
75308 public function ask(OutputInterface $output, $question, $default = null, array $autocomplete = null)
75309 {
75310 $output->write($question);
75311
75312 $inputStream = $this->inputStream ?: STDIN;
75313
75314 if (null === $autocomplete || !$this->hasSttyAvailable()) {
75315 $ret = fgets($inputStream, 4096);
75316 if (false === $ret) {
75317 throw new \RuntimeException('Aborted');
75318 }
75319 $ret = trim($ret);
75320 } else {
75321 $ret = '';
75322
75323 $i = 0;
75324 $ofs = -1;
75325 $matches = $autocomplete;
75326 $numMatches = count($matches);
75327
75328 $sttyMode = shell_exec('stty -g');
75329
75330 // Disable icanon (so we can fread each keypress) and echo (we'll do echoing here instead)
75331 shell_exec('stty -icanon -echo');
75332
75333 // Add highlighted text style
75334 $output->getFormatter()->setStyle('hl', new OutputFormatterStyle('black', 'white'));
75335
75336 // Read a keypress
75337 while (!feof($inputStream)) {
75338 $c = fread($inputStream, 1);
75339
75340 // Backspace Character
75341 if ("\177" === $c) {
75342 if (0 === $numMatches && 0 !== $i) {
75343 $i--;
75344 // Move cursor backwards
75345 $output->write("\033[1D");
75346 }
75347
75348 if ($i === 0) {
75349 $ofs = -1;
75350 $matches = $autocomplete;
75351 $numMatches = count($matches);
75352 } else {
75353 $numMatches = 0;
75354 }
75355
75356 // Pop the last character off the end of our string
75357 $ret = substr($ret, 0, $i);
75358 } elseif ("\033" === $c) { // Did we read an escape sequence?
75359 $c .= fread($inputStream, 2);
75360
75361 // A = Up Arrow. B = Down Arrow
75362 if ('A' === $c[2] || 'B' === $c[2]) {
75363 if ('A' === $c[2] && -1 === $ofs) {
75364 $ofs = 0;
75365 }
75366
75367 if (0 === $numMatches) {
75368 continue;
75369 }
75370
75371 $ofs += ('A' === $c[2]) ? -1 : 1;
75372 $ofs = ($numMatches + $ofs) % $numMatches;
75373 }
75374 } elseif (ord($c) < 32) {
75375 if ("\t" === $c || "\n" === $c) {
75376 if ($numMatches > 0 && -1 !== $ofs) {
75377 $ret = $matches[$ofs];
75378 // Echo out remaining chars for current match
75379 $output->write(substr($ret, $i));
75380 $i = strlen($ret);
75381 }
75382
75383 if ("\n" === $c) {
75384 $output->write($c);
75385 break;
75386 }
75387
75388 $numMatches = 0;
75389 }
75390
75391 continue;
75392 } else {
75393 $output->write($c);
75394 $ret .= $c;
75395 $i++;
75396
75397 $numMatches = 0;
75398 $ofs = 0;
75399
75400 foreach ($autocomplete as $value) {
75401 // If typed characters match the beginning chunk of value (e.g. [AcmeDe]moBundle)
75402 if (0 === strpos($value, $ret) && $i !== strlen($value)) {
75403 $matches[$numMatches++] = $value;
75404 }
75405 }
75406 }
75407
75408 // Erase characters from cursor to end of line
75409 $output->write("\033[K");
75410
75411 if ($numMatches > 0 && -1 !== $ofs) {
75412 // Save cursor position
75413 $output->write("\0337");
75414 // Write highlighted text
75415 $output->write('<hl>'.substr($matches[$ofs], $i).'</hl>');
75416 // Restore cursor position
75417 $output->write("\0338");
75418 }
75419 }
75420
75421 // Reset stty so it behaves normally again
75422 shell_exec(sprintf('stty %s', $sttyMode));
75423 }
75424
75425 return strlen($ret) > 0 ? $ret : $default;
75426 }
75427
75428 /**
75429 * Asks a confirmation to the user.
75430 *
75431 * The question will be asked until the user answers by nothing, yes, or no.
75432 *
75433 * @param OutputInterface $output An Output instance
75434 * @param string|array $question The question to ask
75435 * @param Boolean $default The default answer if the user enters nothing
75436 *
75437 * @return Boolean true if the user has confirmed, false otherwise
75438 */
75439 public function askConfirmation(OutputInterface $output, $question, $default = true)
75440 {
75441 $answer = 'z';
75442 while ($answer && !in_array(strtolower($answer[0]), array('y', 'n'))) {
75443 $answer = $this->ask($output, $question);
75444 }
75445
75446 if (false === $default) {
75447 return $answer && 'y' == strtolower($answer[0]);
75448 }
75449
75450 return !$answer || 'y' == strtolower($answer[0]);
75451 }
75452
75453 /**
75454 * Asks a question to the user, the response is hidden
75455 *
75456 * @param OutputInterface $output An Output instance
75457 * @param string|array $question The question
75458 * @param Boolean $fallback In case the response can not be hidden, whether to fallback on non-hidden question or not
75459 *
75460 * @return string The answer
75461 *
75462 * @throws \RuntimeException In case the fallback is deactivated and the response can not be hidden
75463 */
75464 public function askHiddenResponse(OutputInterface $output, $question, $fallback = true)
75465 {
75466 if (defined('PHP_WINDOWS_VERSION_BUILD')) {
75467 $exe = __DIR__.'/../Resources/bin/hiddeninput.exe';
75468
75469 // handle code running from a phar
75470 if ('phar:' === substr(__FILE__, 0, 5)) {
75471 $tmpExe = sys_get_temp_dir().'/hiddeninput.exe';
75472 copy($exe, $tmpExe);
75473 $exe = $tmpExe;
75474 }
75475
75476 $output->write($question);
75477 $value = rtrim(shell_exec($exe));
75478 $output->writeln('');
75479
75480 if (isset($tmpExe)) {
75481 unlink($tmpExe);
75482 }
75483
75484 return $value;
75485 }
75486
75487 if ($this->hasSttyAvailable()) {
75488 $output->write($question);
75489
75490 $sttyMode = shell_exec('stty -g');
75491
75492 shell_exec('stty -echo');
75493 $value = fgets($this->inputStream ?: STDIN, 4096);
75494 shell_exec(sprintf('stty %s', $sttyMode));
75495
75496 if (false === $value) {
75497 throw new \RuntimeException('Aborted');
75498 }
75499
75500 $value = trim($value);
75501 $output->writeln('');
75502
75503 return $value;
75504 }
75505
75506 if (false !== $shell = $this->getShell()) {
75507 $output->write($question);
75508 $readCmd = $shell === 'csh' ? 'set mypassword = $<' : 'read -r mypassword';
75509 $command = sprintf("/usr/bin/env %s -c 'stty -echo; %s; stty echo; echo \$mypassword'", $shell, $readCmd);
75510 $value = rtrim(shell_exec($command));
75511 $output->writeln('');
75512
75513 return $value;
75514 }
75515
75516 if ($fallback) {
75517 return $this->ask($output, $question);
75518 }
75519
75520 throw new \RuntimeException('Unable to hide the response');
75521 }
75522
75523 /**
75524 * Asks for a value and validates the response.
75525 *
75526 * The validator receives the data to validate. It must return the
75527 * validated data when the data is valid and throw an exception
75528 * otherwise.
75529 *
75530 * @param OutputInterface $output An Output instance
75531 * @param string|array $question The question to ask
75532 * @param callable $validator A PHP callback
75533 * @param integer $attempts Max number of times to ask before giving up (false by default, which means infinite)
75534 * @param string $default The default answer if none is given by the user
75535 * @param array $autocomplete List of values to autocomplete
75536 *
75537 * @return mixed
75538 *
75539 * @throws \Exception When any of the validators return an error
75540 */
75541 public function askAndValidate(OutputInterface $output, $question, $validator, $attempts = false, $default = null, array $autocomplete = null)
75542 {
75543 $that = $this;
75544
75545 $interviewer = function() use ($output, $question, $default, $autocomplete, $that) {
75546 return $that->ask($output, $question, $default, $autocomplete);
75547 };
75548
75549 return $this->validateAttempts($interviewer, $output, $validator, $attempts);
75550 }
75551
75552 /**
75553 * Asks for a value, hide and validates the response.
75554 *
75555 * The validator receives the data to validate. It must return the
75556 * validated data when the data is valid and throw an exception
75557 * otherwise.
75558 *
75559 * @param OutputInterface $output An Output instance
75560 * @param string|array $question The question to ask
75561 * @param callable $validator A PHP callback
75562 * @param integer $attempts Max number of times to ask before giving up (false by default, which means infinite)
75563 * @param Boolean $fallback In case the response can not be hidden, whether to fallback on non-hidden question or not
75564 *
75565 * @return string The response
75566 *
75567 * @throws \Exception When any of the validators return an error
75568 * @throws \RuntimeException In case the fallback is deactivated and the response can not be hidden
75569 *
75570 */
75571 public function askHiddenResponseAndValidate(OutputInterface $output, $question, $validator, $attempts = false, $fallback = true)
75572 {
75573 $that = $this;
75574
75575 $interviewer = function() use ($output, $question, $fallback, $that) {
75576 return $that->askHiddenResponse($output, $question, $fallback);
75577 };
75578
75579 return $this->validateAttempts($interviewer, $output, $validator, $attempts);
75580 }
75581
75582 /**
75583 * Sets the input stream to read from when interacting with the user.
75584 *
75585 * This is mainly useful for testing purpose.
75586 *
75587 * @param resource $stream The input stream
75588 */
75589 public function setInputStream($stream)
75590 {
75591 $this->inputStream = $stream;
75592 }
75593
75594 /**
75595 * Returns the helper's input stream
75596 *
75597 * @return string
75598 */
75599 public function getInputStream()
75600 {
75601 return $this->inputStream;
75602 }
75603
75604 /**
75605 * {@inheritDoc}
75606 */
75607 public function getName()
75608 {
75609 return 'dialog';
75610 }
75611
75612 /**
75613 * Return a valid unix shell
75614 *
75615 * @return string|Boolean The valid shell name, false in case no valid shell is found
75616 */
75617 private function getShell()
75618 {
75619 if (null !== self::$shell) {
75620 return self::$shell;
75621 }
75622
75623 self::$shell = false;
75624
75625 if (file_exists('/usr/bin/env')) {
75626 // handle other OSs with bash/zsh/ksh/csh if available to hide the answer
75627 $test = "/usr/bin/env %s -c 'echo OK' 2> /dev/null";
75628 foreach (array('bash', 'zsh', 'ksh', 'csh') as $sh) {
75629 if ('OK' === rtrim(shell_exec(sprintf($test, $sh)))) {
75630 self::$shell = $sh;
75631 break;
75632 }
75633 }
75634 }
75635
75636 return self::$shell;
75637 }
75638
75639 private function hasSttyAvailable()
75640 {
75641 if (null !== self::$stty) {
75642 return self::$stty;
75643 }
75644
75645 exec('stty 2>&1', $output, $exitcode);
75646
75647 return self::$stty = $exitcode === 0;
75648 }
75649
75650 /**
75651 * Validate an attempt
75652 *
75653 * @param callable $interviewer A callable that will ask for a question and return the result
75654 * @param OutputInterface $output An Output instance
75655 * @param callable $validator A PHP callback
75656 * @param integer $attempts Max number of times to ask before giving up ; false will ask infinitely
75657 *
75658 * @return string The validated response
75659 *
75660 * @throws \Exception In case the max number of attempts has been reached and no valid response has been given
75661 */
75662 private function validateAttempts($interviewer, OutputInterface $output, $validator, $attempts)
75663 {
75664 $error = null;
75665 while (false === $attempts || $attempts--) {
75666 if (null !== $error) {
75667 $output->writeln($this->getHelperSet()->get('formatter')->formatBlock($error->getMessage(), 'error'));
75668 }
75669
75670 try {
75671 return call_user_func($validator, $interviewer());
75672 } catch (\Exception $error) {
75673 }
75674 }
75675
75676 throw $error;
75677 }
75678 }
75679 <?php
75680
75681 /*
75682 * This file is part of the Symfony package.
75683 *
75684 * (c) Fabien Potencier <fabien@symfony.com>
75685 *
75686 * For the full copyright and license information, please view the LICENSE
75687 * file that was distributed with this source code.
75688 */
75689
75690 namespace Symfony\Component\Console\Helper;
75691
75692 /**
75693 * HelperInterface is the interface all helpers must implement.
75694 *
75695 * @author Fabien Potencier <fabien@symfony.com>
75696 *
75697 * @api
75698 */
75699 interface HelperInterface
75700 {
75701 /**
75702 * Sets the helper set associated with this helper.
75703 *
75704 * @param HelperSet $helperSet A HelperSet instance
75705 *
75706 * @api
75707 */
75708 public function setHelperSet(HelperSet $helperSet = null);
75709
75710 /**
75711 * Gets the helper set associated with this helper.
75712 *
75713 * @return HelperSet A HelperSet instance
75714 *
75715 * @api
75716 */
75717 public function getHelperSet();
75718
75719 /**
75720 * Returns the canonical name of this helper.
75721 *
75722 * @return string The canonical name
75723 *
75724 * @api
75725 */
75726 public function getName();
75727 }
75728 <?php
75729
75730 /*
75731 * This file is part of the Symfony package.
75732 *
75733 * (c) Fabien Potencier <fabien@symfony.com>
75734 *
75735 * For the full copyright and license information, please view the LICENSE
75736 * file that was distributed with this source code.
75737 */
75738
75739 namespace Symfony\Component\Console\Helper;
75740
75741 use Symfony\Component\Console\Formatter\OutputFormatter;
75742
75743 /**
75744 * The Formatter class provides helpers to format messages.
75745 *
75746 * @author Fabien Potencier <fabien@symfony.com>
75747 */
75748 class FormatterHelper extends Helper
75749 {
75750 /**
75751 * Formats a message within a section.
75752 *
75753 * @param string $section The section name
75754 * @param string $message The message
75755 * @param string $style The style to apply to the section
75756 *
75757 * @return string The format section
75758 */
75759 public function formatSection($section, $message, $style = 'info')
75760 {
75761 return sprintf('<%s>[%s]</%s> %s', $style, $section, $style, $message);
75762 }
75763
75764 /**
75765 * Formats a message as a block of text.
75766 *
75767 * @param string|array $messages The message to write in the block
75768 * @param string $style The style to apply to the whole block
75769 * @param Boolean $large Whether to return a large block
75770 *
75771 * @return string The formatter message
75772 */
75773 public function formatBlock($messages, $style, $large = false)
75774 {
75775 $messages = (array) $messages;
75776
75777 $len = 0;
75778 $lines = array();
75779 foreach ($messages as $message) {
75780 $message = OutputFormatter::escape($message);
75781 $lines[] = sprintf($large ? ' %s ' : ' %s ', $message);
75782 $len = max($this->strlen($message) + ($large ? 4 : 2), $len);
75783 }
75784
75785 $messages = $large ? array(str_repeat(' ', $len)) : array();
75786 foreach ($lines as $line) {
75787 $messages[] = $line.str_repeat(' ', $len - $this->strlen($line));
75788 }
75789 if ($large) {
75790 $messages[] = str_repeat(' ', $len);
75791 }
75792
75793 foreach ($messages as &$message) {
75794 $message = sprintf('<%s>%s</%s>', $style, $message, $style);
75795 }
75796
75797 return implode("\n", $messages);
75798 }
75799
75800 /**
75801 * {@inheritDoc}
75802 */
75803 public function getName()
75804 {
75805 return 'formatter';
75806 }
75807 }
75808 <?php
75809
75810 /*
75811 * This file is part of the Symfony package.
75812 *
75813 * (c) Fabien Potencier <fabien@symfony.com>
75814 *
75815 * For the full copyright and license information, please view the LICENSE
75816 * file that was distributed with this source code.
75817 */
75818
75819 namespace Symfony\Component\Console;
75820
75821 /**
75822 * Contains all events dispatched by an Application.
75823 *
75824 * @author Francesco Levorato <git@flevour.net>
75825 */
75826 final class ConsoleEvents
75827 {
75828 /**
75829 * The COMMAND event allows you to attach listeners before any command is
75830 * executed by the console. It also allows you to modify the command, input and output
75831 * before they are handled to the command.
75832 *
75833 * The event listener method receives a Symfony\Component\Console\Event\ConsoleCommandEvent
75834 * instance.
75835 *
75836 * @var string
75837 */
75838 const COMMAND = 'console.command';
75839
75840 /**
75841 * The TERMINATE event allows you to attach listeners after a command is
75842 * executed by the console.
75843 *
75844 * The event listener method receives a Symfony\Component\Console\Event\ConsoleTerminateEvent
75845 * instance.
75846 *
75847 * @var string
75848 */
75849 const TERMINATE = 'console.terminate';
75850
75851 /**
75852 * The EXCEPTION event occurs when an uncaught exception appears.
75853 *
75854 * This event allows you to deal with the exception or
75855 * to modify the thrown exception. The event listener method receives
75856 * a Symfony\Component\Console\Event\ConsoleExceptionEvent
75857 * instance.
75858 *
75859 * @var string
75860 */
75861 const EXCEPTION = 'console.exception';
75862 }
75863 <?php
75864
75865 /*
75866 * This file is part of the Symfony package.
75867 *
75868 * (c) Fabien Potencier <fabien@symfony.com>
75869 *
75870 * For the full copyright and license information, please view the LICENSE
75871 * file that was distributed with this source code.
75872 */
75873
75874 namespace Symfony\Component\Console\Formatter;
75875
75876 /**
75877 * Formatter style interface for defining styles.
75878 *
75879 * @author Konstantin Kudryashov <ever.zet@gmail.com>
75880 *
75881 * @api
75882 */
75883 interface OutputFormatterStyleInterface
75884 {
75885 /**
75886 * Sets style foreground color.
75887 *
75888 * @param string $color The color name
75889 *
75890 * @api
75891 */
75892 public function setForeground($color = null);
75893
75894 /**
75895 * Sets style background color.
75896 *
75897 * @param string $color The color name
75898 *
75899 * @api
75900 */
75901 public function setBackground($color = null);
75902
75903 /**
75904 * Sets some specific style option.
75905 *
75906 * @param string $option The option name
75907 *
75908 * @api
75909 */
75910 public function setOption($option);
75911
75912 /**
75913 * Unsets some specific style option.
75914 *
75915 * @param string $option The option name
75916 */
75917 public function unsetOption($option);
75918
75919 /**
75920 * Sets multiple style options at once.
75921 *
75922 * @param array $options
75923 */
75924 public function setOptions(array $options);
75925
75926 /**
75927 * Applies the style to a given text.
75928 *
75929 * @param string $text The text to style
75930 *
75931 * @return string
75932 */
75933 public function apply($text);
75934 }
75935 <?php
75936
75937 /*
75938 * This file is part of the Symfony package.
75939 *
75940 * (c) Fabien Potencier <fabien@symfony.com>
75941 *
75942 * For the full copyright and license information, please view the LICENSE
75943 * file that was distributed with this source code.
75944 */
75945
75946 namespace Symfony\Component\Console\Formatter;
75947
75948 /**
75949 * Formatter style class for defining styles.
75950 *
75951 * @author Konstantin Kudryashov <ever.zet@gmail.com>
75952 *
75953 * @api
75954 */
75955 class OutputFormatterStyle implements OutputFormatterStyleInterface
75956 {
75957 private static $availableForegroundColors = array(
75958 'black' => 30,
75959 'red' => 31,
75960 'green' => 32,
75961 'yellow' => 33,
75962 'blue' => 34,
75963 'magenta' => 35,
75964 'cyan' => 36,
75965 'white' => 37
75966 );
75967 private static $availableBackgroundColors = array(
75968 'black' => 40,
75969 'red' => 41,
75970 'green' => 42,
75971 'yellow' => 43,
75972 'blue' => 44,
75973 'magenta' => 45,
75974 'cyan' => 46,
75975 'white' => 47
75976 );
75977 private static $availableOptions = array(
75978 'bold' => 1,
75979 'underscore' => 4,
75980 'blink' => 5,
75981 'reverse' => 7,
75982 'conceal' => 8
75983 );
75984
75985 private $foreground;
75986 private $background;
75987 private $options = array();
75988
75989 /**
75990 * Initializes output formatter style.
75991 *
75992 * @param string $foreground The style foreground color name
75993 * @param string $background The style background color name
75994 * @param array $options The style options
75995 *
75996 * @api
75997 */
75998 public function __construct($foreground = null, $background = null, array $options = array())
75999 {
76000 if (null !== $foreground) {
76001 $this->setForeground($foreground);
76002 }
76003 if (null !== $background) {
76004 $this->setBackground($background);
76005 }
76006 if (count($options)) {
76007 $this->setOptions($options);
76008 }
76009 }
76010
76011 /**
76012 * Sets style foreground color.
76013 *
76014 * @param string $color The color name
76015 *
76016 * @throws \InvalidArgumentException When the color name isn't defined
76017 *
76018 * @api
76019 */
76020 public function setForeground($color = null)
76021 {
76022 if (null === $color) {
76023 $this->foreground = null;
76024
76025 return;
76026 }
76027
76028 if (!isset(static::$availableForegroundColors[$color])) {
76029 throw new \InvalidArgumentException(sprintf(
76030 'Invalid foreground color specified: "%s". Expected one of (%s)',
76031 $color,
76032 implode(', ', array_keys(static::$availableForegroundColors))
76033 ));
76034 }
76035
76036 $this->foreground = static::$availableForegroundColors[$color];
76037 }
76038
76039 /**
76040 * Sets style background color.
76041 *
76042 * @param string $color The color name
76043 *
76044 * @throws \InvalidArgumentException When the color name isn't defined
76045 *
76046 * @api
76047 */
76048 public function setBackground($color = null)
76049 {
76050 if (null === $color) {
76051 $this->background = null;
76052
76053 return;
76054 }
76055
76056 if (!isset(static::$availableBackgroundColors[$color])) {
76057 throw new \InvalidArgumentException(sprintf(
76058 'Invalid background color specified: "%s". Expected one of (%s)',
76059 $color,
76060 implode(', ', array_keys(static::$availableBackgroundColors))
76061 ));
76062 }
76063
76064 $this->background = static::$availableBackgroundColors[$color];
76065 }
76066
76067 /**
76068 * Sets some specific style option.
76069 *
76070 * @param string $option The option name
76071 *
76072 * @throws \InvalidArgumentException When the option name isn't defined
76073 *
76074 * @api
76075 */
76076 public function setOption($option)
76077 {
76078 if (!isset(static::$availableOptions[$option])) {
76079 throw new \InvalidArgumentException(sprintf(
76080 'Invalid option specified: "%s". Expected one of (%s)',
76081 $option,
76082 implode(', ', array_keys(static::$availableOptions))
76083 ));
76084 }
76085
76086 if (false === array_search(static::$availableOptions[$option], $this->options)) {
76087 $this->options[] = static::$availableOptions[$option];
76088 }
76089 }
76090
76091 /**
76092 * Unsets some specific style option.
76093 *
76094 * @param string $option The option name
76095 *
76096 * @throws \InvalidArgumentException When the option name isn't defined
76097 *
76098 */
76099 public function unsetOption($option)
76100 {
76101 if (!isset(static::$availableOptions[$option])) {
76102 throw new \InvalidArgumentException(sprintf(
76103 'Invalid option specified: "%s". Expected one of (%s)',
76104 $option,
76105 implode(', ', array_keys(static::$availableOptions))
76106 ));
76107 }
76108
76109 $pos = array_search(static::$availableOptions[$option], $this->options);
76110 if (false !== $pos) {
76111 unset($this->options[$pos]);
76112 }
76113 }
76114
76115 /**
76116 * Sets multiple style options at once.
76117 *
76118 * @param array $options
76119 */
76120 public function setOptions(array $options)
76121 {
76122 $this->options = array();
76123
76124 foreach ($options as $option) {
76125 $this->setOption($option);
76126 }
76127 }
76128
76129 /**
76130 * Applies the style to a given text.
76131 *
76132 * @param string $text The text to style
76133 *
76134 * @return string
76135 */
76136 public function apply($text)
76137 {
76138 $codes = array();
76139
76140 if (null !== $this->foreground) {
76141 $codes[] = $this->foreground;
76142 }
76143 if (null !== $this->background) {
76144 $codes[] = $this->background;
76145 }
76146 if (count($this->options)) {
76147 $codes = array_merge($codes, $this->options);
76148 }
76149
76150 if (0 === count($codes)) {
76151 return $text;
76152 }
76153
76154 return sprintf("\033[%sm%s\033[0m", implode(';', $codes), $text);
76155 }
76156 }
76157 <?php
76158
76159 /*
76160 * This file is part of the Symfony package.
76161 *
76162 * (c) Fabien Potencier <fabien@symfony.com>
76163 *
76164 * For the full copyright and license information, please view the LICENSE
76165 * file that was distributed with this source code.
76166 */
76167
76168 namespace Symfony\Component\Console\Formatter;
76169
76170 /**
76171 * Formatter interface for console output.
76172 *
76173 * @author Konstantin Kudryashov <ever.zet@gmail.com>
76174 *
76175 * @api
76176 */
76177 interface OutputFormatterInterface
76178 {
76179 /**
76180 * Sets the decorated flag.
76181 *
76182 * @param Boolean $decorated Whether to decorate the messages or not
76183 *
76184 * @api
76185 */
76186 public function setDecorated($decorated);
76187
76188 /**
76189 * Gets the decorated flag.
76190 *
76191 * @return Boolean true if the output will decorate messages, false otherwise
76192 *
76193 * @api
76194 */
76195 public function isDecorated();
76196
76197 /**
76198 * Sets a new style.
76199 *
76200 * @param string $name The style name
76201 * @param OutputFormatterStyleInterface $style The style instance
76202 *
76203 * @api
76204 */
76205 public function setStyle($name, OutputFormatterStyleInterface $style);
76206
76207 /**
76208 * Checks if output formatter has style with specified name.
76209 *
76210 * @param string $name
76211 *
76212 * @return Boolean
76213 *
76214 * @api
76215 */
76216 public function hasStyle($name);
76217
76218 /**
76219 * Gets style options from style with specified name.
76220 *
76221 * @param string $name
76222 *
76223 * @return OutputFormatterStyleInterface
76224 *
76225 * @api
76226 */
76227 public function getStyle($name);
76228
76229 /**
76230 * Formats a message according to the given styles.
76231 *
76232 * @param string $message The message to style
76233 *
76234 * @return string The styled message
76235 *
76236 * @api
76237 */
76238 public function format($message);
76239 }
76240 <?php
76241
76242 /*
76243 * This file is part of the Symfony package.
76244 *
76245 * (c) Fabien Potencier <fabien@symfony.com>
76246 *
76247 * For the full copyright and license information, please view the LICENSE
76248 * file that was distributed with this source code.
76249 */
76250
76251 namespace Symfony\Component\Console\Formatter;
76252
76253 /**
76254 * Formatter class for console output.
76255 *
76256 * @author Konstantin Kudryashov <ever.zet@gmail.com>
76257 *
76258 * @api
76259 */
76260 class OutputFormatter implements OutputFormatterInterface
76261 {
76262 /**
76263 * The pattern to phrase the format.
76264 */
76265 const FORMAT_PATTERN = '#(\\\\?)<(/?)([a-z][a-z0-9_=;-]+)?>((?: [^<\\\\]+ | (?!<(?:/?[a-z]|/>)). | .(?<=\\\\<) )*)#isx';
76266
76267 private $decorated;
76268 private $styles = array();
76269 private $styleStack;
76270
76271 /**
76272 * Escapes "<" special char in given text.
76273 *
76274 * @param string $text Text to escape
76275 *
76276 * @return string Escaped text
76277 */
76278 public static function escape($text)
76279 {
76280 return preg_replace('/([^\\\\]?)</is', '$1\\<', $text);
76281 }
76282
76283 /**
76284 * Initializes console output formatter.
76285 *
76286 * @param Boolean $decorated Whether this formatter should actually decorate strings
76287 * @param FormatterStyle[] $styles Array of "name => FormatterStyle" instances
76288 *
76289 * @api
76290 */
76291 public function __construct($decorated = null, array $styles = array())
76292 {
76293 $this->decorated = (Boolean) $decorated;
76294
76295 $this->setStyle('error', new OutputFormatterStyle('white', 'red'));
76296 $this->setStyle('info', new OutputFormatterStyle('green'));
76297 $this->setStyle('comment', new OutputFormatterStyle('yellow'));
76298 $this->setStyle('question', new OutputFormatterStyle('black', 'cyan'));
76299
76300 foreach ($styles as $name => $style) {
76301 $this->setStyle($name, $style);
76302 }
76303
76304 $this->styleStack = new OutputFormatterStyleStack();
76305 }
76306
76307 /**
76308 * Sets the decorated flag.
76309 *
76310 * @param Boolean $decorated Whether to decorate the messages or not
76311 *
76312 * @api
76313 */
76314 public function setDecorated($decorated)
76315 {
76316 $this->decorated = (Boolean) $decorated;
76317 }
76318
76319 /**
76320 * Gets the decorated flag.
76321 *
76322 * @return Boolean true if the output will decorate messages, false otherwise
76323 *
76324 * @api
76325 */
76326 public function isDecorated()
76327 {
76328 return $this->decorated;
76329 }
76330
76331 /**
76332 * Sets a new style.
76333 *
76334 * @param string $name The style name
76335 * @param OutputFormatterStyleInterface $style The style instance
76336 *
76337 * @api
76338 */
76339 public function setStyle($name, OutputFormatterStyleInterface $style)
76340 {
76341 $this->styles[strtolower($name)] = $style;
76342 }
76343
76344 /**
76345 * Checks if output formatter has style with specified name.
76346 *
76347 * @param string $name
76348 *
76349 * @return Boolean
76350 *
76351 * @api
76352 */
76353 public function hasStyle($name)
76354 {
76355 return isset($this->styles[strtolower($name)]);
76356 }
76357
76358 /**
76359 * Gets style options from style with specified name.
76360 *
76361 * @param string $name
76362 *
76363 * @return OutputFormatterStyleInterface
76364 *
76365 * @throws \InvalidArgumentException When style isn't defined
76366 *
76367 * @api
76368 */
76369 public function getStyle($name)
76370 {
76371 if (!$this->hasStyle($name)) {
76372 throw new \InvalidArgumentException(sprintf('Undefined style: %s', $name));
76373 }
76374
76375 return $this->styles[strtolower($name)];
76376 }
76377
76378 /**
76379 * Formats a message according to the given styles.
76380 *
76381 * @param string $message The message to style
76382 *
76383 * @return string The styled message
76384 *
76385 * @api
76386 */
76387 public function format($message)
76388 {
76389 $message = preg_replace_callback(self::FORMAT_PATTERN, array($this, 'replaceStyle'), $message);
76390
76391 return str_replace('\\<', '<', $message);
76392 }
76393
76394 /**
76395 * @return OutputFormatterStyleStack
76396 */
76397 public function getStyleStack()
76398 {
76399 return $this->styleStack;
76400 }
76401
76402 /**
76403 * Replaces style of the output.
76404 *
76405 * @param array $match
76406 *
76407 * @return string The replaced style
76408 */
76409 private function replaceStyle($match)
76410 {
76411 // we got "\<" escaped char
76412 if ('\\' === $match[1]) {
76413 return $this->applyCurrentStyle($match[0]);
76414 }
76415
76416 if ('' === $match[3]) {
76417 if ('/' === $match[2]) {
76418 // we got "</>" tag
76419 $this->styleStack->pop();
76420
76421 return $this->applyCurrentStyle($match[4]);
76422 }
76423
76424 // we got "<>" tag
76425 return '<>'.$this->applyCurrentStyle($match[4]);
76426 }
76427
76428 if (isset($this->styles[strtolower($match[3])])) {
76429 $style = $this->styles[strtolower($match[3])];
76430 } else {
76431 $style = $this->createStyleFromString($match[3]);
76432
76433 if (false === $style) {
76434 return $this->applyCurrentStyle($match[0]);
76435 }
76436 }
76437
76438 if ('/' === $match[2]) {
76439 $this->styleStack->pop($style);
76440 } else {
76441 $this->styleStack->push($style);
76442 }
76443
76444 return $this->applyCurrentStyle($match[4]);
76445 }
76446
76447 /**
76448 * Tries to create new style instance from string.
76449 *
76450 * @param string $string
76451 *
76452 * @return OutputFormatterStyle|Boolean false if string is not format string
76453 */
76454 private function createStyleFromString($string)
76455 {
76456 if (!preg_match_all('/([^=]+)=([^;]+)(;|$)/', strtolower($string), $matches, PREG_SET_ORDER)) {
76457 return false;
76458 }
76459
76460 $style = new OutputFormatterStyle();
76461 foreach ($matches as $match) {
76462 array_shift($match);
76463
76464 if ('fg' == $match[0]) {
76465 $style->setForeground($match[1]);
76466 } elseif ('bg' == $match[0]) {
76467 $style->setBackground($match[1]);
76468 } else {
76469 $style->setOption($match[1]);
76470 }
76471 }
76472
76473 return $style;
76474 }
76475
76476 /**
76477 * Applies current style from stack to text, if must be applied.
76478 *
76479 * @param string $text Input text
76480 *
76481 * @return string Styled text
76482 */
76483 private function applyCurrentStyle($text)
76484 {
76485 return $this->isDecorated() && strlen($text) > 0 ? $this->styleStack->getCurrent()->apply($text) : $text;
76486 }
76487 }
76488 <?php
76489
76490 /*
76491 * This file is part of the Symfony package.
76492 *
76493 * (c) Fabien Potencier <fabien@symfony.com>
76494 *
76495 * For the full copyright and license information, please view the LICENSE
76496 * file that was distributed with this source code.
76497 */
76498
76499 namespace Symfony\Component\Console\Formatter;
76500
76501 /**
76502 * @author Jean-François Simon <contact@jfsimon.fr>
76503 */
76504 class OutputFormatterStyleStack
76505 {
76506 /**
76507 * @var OutputFormatterStyleInterface[]
76508 */
76509 private $styles;
76510
76511 /**
76512 * @var OutputFormatterStyleInterface
76513 */
76514 private $emptyStyle;
76515
76516 /**
76517 * Constructor.
76518 *
76519 * @param OutputFormatterStyleInterface $emptyStyle
76520 */
76521 public function __construct(OutputFormatterStyleInterface $emptyStyle = null)
76522 {
76523 $this->emptyStyle = $emptyStyle ?: new OutputFormatterStyle();
76524 $this->reset();
76525 }
76526
76527 /**
76528 * Resets stack (ie. empty internal arrays).
76529 */
76530 public function reset()
76531 {
76532 $this->styles = array();
76533 }
76534
76535 /**
76536 * Pushes a style in the stack.
76537 *
76538 * @param OutputFormatterStyleInterface $style
76539 */
76540 public function push(OutputFormatterStyleInterface $style)
76541 {
76542 $this->styles[] = $style;
76543 }
76544
76545 /**
76546 * Pops a style from the stack.
76547 *
76548 * @param OutputFormatterStyleInterface $style
76549 *
76550 * @return OutputFormatterStyleInterface
76551 *
76552 * @throws \InvalidArgumentException When style tags incorrectly nested
76553 */
76554 public function pop(OutputFormatterStyleInterface $style = null)
76555 {
76556 if (empty($this->styles)) {
76557 return $this->emptyStyle;
76558 }
76559
76560 if (null === $style) {
76561 return array_pop($this->styles);
76562 }
76563
76564 foreach (array_reverse($this->styles, true) as $index => $stackedStyle) {
76565 if ($style->apply('') === $stackedStyle->apply('')) {
76566 $this->styles = array_slice($this->styles, 0, $index);
76567
76568 return $stackedStyle;
76569 }
76570 }
76571
76572 throw new \InvalidArgumentException('Incorrectly nested style tag found.');
76573 }
76574
76575 /**
76576 * Computes current style with stacks top codes.
76577 *
76578 * @return OutputFormatterStyle
76579 */
76580 public function getCurrent()
76581 {
76582 if (empty($this->styles)) {
76583 return $this->emptyStyle;
76584 }
76585
76586 return $this->styles[count($this->styles)-1];
76587 }
76588
76589 /**
76590 * @param OutputFormatterStyleInterface $emptyStyle
76591 *
76592 * @return OutputFormatterStyleStack
76593 */
76594 public function setEmptyStyle(OutputFormatterStyleInterface $emptyStyle)
76595 {
76596 $this->emptyStyle = $emptyStyle;
76597
76598 return $this;
76599 }
76600
76601 /**
76602 * @return OutputFormatterStyleInterface
76603 */
76604 public function getEmptyStyle()
76605 {
76606 return $this->emptyStyle;
76607 }
76608 }
76609 <?php
76610
76611 /*
76612 * This file is part of the Symfony package.
76613 *
76614 * (c) Fabien Potencier <fabien@symfony.com>
76615 *
76616 * For the full copyright and license information, please view the LICENSE
76617 * file that was distributed with this source code.
76618 */
76619
76620 namespace Symfony\Component\Console\Output;
76621
76622 use Symfony\Component\Console\Formatter\OutputFormatterInterface;
76623 use Symfony\Component\Console\Formatter\OutputFormatter;
76624
76625 /**
76626 * Base class for output classes.
76627 *
76628 * There are five levels of verbosity:
76629 *
76630 * * normal: no option passed (normal output)
76631 * * verbose: -v (more output)
76632 * * very verbose: -vv (highly extended output)
76633 * * debug: -vvv (all debug output)
76634 * * quiet: -q (no output)
76635 *
76636 * @author Fabien Potencier <fabien@symfony.com>
76637 *
76638 * @api
76639 */
76640 abstract class Output implements OutputInterface
76641 {
76642 private $verbosity;
76643 private $formatter;
76644
76645 /**
76646 * Constructor.
76647 *
76648 * @param integer $verbosity The verbosity level (one of the VERBOSITY constants in OutputInterface)
76649 * @param Boolean $decorated Whether to decorate messages
76650 * @param OutputFormatterInterface|null $formatter Output formatter instance (null to use default OutputFormatter)
76651 *
76652 * @api
76653 */
76654 public function __construct($verbosity = self::VERBOSITY_NORMAL, $decorated = false, OutputFormatterInterface $formatter = null)
76655 {
76656 $this->verbosity = null === $verbosity ? self::VERBOSITY_NORMAL : $verbosity;
76657 $this->formatter = null === $formatter ? new OutputFormatter() : $formatter;
76658 $this->formatter->setDecorated($decorated);
76659 }
76660
76661 /**
76662 * {@inheritdoc}
76663 */
76664 public function setFormatter(OutputFormatterInterface $formatter)
76665 {
76666 $this->formatter = $formatter;
76667 }
76668
76669 /**
76670 * {@inheritdoc}
76671 */
76672 public function getFormatter()
76673 {
76674 return $this->formatter;
76675 }
76676
76677 /**
76678 * {@inheritdoc}
76679 */
76680 public function setDecorated($decorated)
76681 {
76682 $this->formatter->setDecorated($decorated);
76683 }
76684
76685 /**
76686 * {@inheritdoc}
76687 */
76688 public function isDecorated()
76689 {
76690 return $this->formatter->isDecorated();
76691 }
76692
76693 /**
76694 * {@inheritdoc}
76695 */
76696 public function setVerbosity($level)
76697 {
76698 $this->verbosity = (int) $level;
76699 }
76700
76701 /**
76702 * {@inheritdoc}
76703 */
76704 public function getVerbosity()
76705 {
76706 return $this->verbosity;
76707 }
76708
76709 /**
76710 * {@inheritdoc}
76711 */
76712 public function writeln($messages, $type = self::OUTPUT_NORMAL)
76713 {
76714 $this->write($messages, true, $type);
76715 }
76716
76717 /**
76718 * {@inheritdoc}
76719 */
76720 public function write($messages, $newline = false, $type = self::OUTPUT_NORMAL)
76721 {
76722 if (self::VERBOSITY_QUIET === $this->verbosity) {
76723 return;
76724 }
76725
76726 $messages = (array) $messages;
76727
76728 foreach ($messages as $message) {
76729 switch ($type) {
76730 case OutputInterface::OUTPUT_NORMAL:
76731 $message = $this->formatter->format($message);
76732 break;
76733 case OutputInterface::OUTPUT_RAW:
76734 break;
76735 case OutputInterface::OUTPUT_PLAIN:
76736 $message = strip_tags($this->formatter->format($message));
76737 break;
76738 default:
76739 throw new \InvalidArgumentException(sprintf('Unknown output type given (%s)', $type));
76740 }
76741
76742 $this->doWrite($message, $newline);
76743 }
76744 }
76745
76746 /**
76747 * Writes a message to the output.
76748 *
76749 * @param string $message A message to write to the output
76750 * @param Boolean $newline Whether to add a newline or not
76751 */
76752 abstract protected function doWrite($message, $newline);
76753 }
76754 <?php
76755
76756 /*
76757 * This file is part of the Symfony package.
76758 *
76759 * (c) Fabien Potencier <fabien@symfony.com>
76760 *
76761 * For the full copyright and license information, please view the LICENSE
76762 * file that was distributed with this source code.
76763 */
76764
76765 namespace Symfony\Component\Console\Output;
76766
76767 use Symfony\Component\Console\Formatter\OutputFormatterInterface;
76768
76769 /**
76770 * StreamOutput writes the output to a given stream.
76771 *
76772 * Usage:
76773 *
76774 * $output = new StreamOutput(fopen('php://stdout', 'w'));
76775 *
76776 * As `StreamOutput` can use any stream, you can also use a file:
76777 *
76778 * $output = new StreamOutput(fopen('/path/to/output.log', 'a', false));
76779 *
76780 * @author Fabien Potencier <fabien@symfony.com>
76781 *
76782 * @api
76783 */
76784 class StreamOutput extends Output
76785 {
76786 private $stream;
76787
76788 /**
76789 * Constructor.
76790 *
76791 * @param mixed $stream A stream resource
76792 * @param integer $verbosity The verbosity level (one of the VERBOSITY constants in OutputInterface)
76793 * @param Boolean|null $decorated Whether to decorate messages (null for auto-guessing)
76794 * @param OutputFormatterInterface|null $formatter Output formatter instance (null to use default OutputFormatter)
76795 *
76796 * @throws \InvalidArgumentException When first argument is not a real stream
76797 *
76798 * @api
76799 */
76800 public function __construct($stream, $verbosity = self::VERBOSITY_NORMAL, $decorated = null, OutputFormatterInterface $formatter = null)
76801 {
76802 if (!is_resource($stream) || 'stream' !== get_resource_type($stream)) {
76803 throw new \InvalidArgumentException('The StreamOutput class needs a stream as its first argument.');
76804 }
76805
76806 $this->stream = $stream;
76807
76808 if (null === $decorated) {
76809 $decorated = $this->hasColorSupport();
76810 }
76811
76812 parent::__construct($verbosity, $decorated, $formatter);
76813 }
76814
76815 /**
76816 * Gets the stream attached to this StreamOutput instance.
76817 *
76818 * @return resource A stream resource
76819 */
76820 public function getStream()
76821 {
76822 return $this->stream;
76823 }
76824
76825 /**
76826 * {@inheritdoc}
76827 */
76828 protected function doWrite($message, $newline)
76829 {
76830 if (false === @fwrite($this->stream, $message.($newline ? PHP_EOL : ''))) {
76831 // @codeCoverageIgnoreStart
76832 // should never happen
76833 throw new \RuntimeException('Unable to write output.');
76834 // @codeCoverageIgnoreEnd
76835 }
76836
76837 fflush($this->stream);
76838 }
76839
76840 /**
76841 * Returns true if the stream supports colorization.
76842 *
76843 * Colorization is disabled if not supported by the stream:
76844 *
76845 * - windows without ansicon and ConEmu
76846 * - non tty consoles
76847 *
76848 * @return Boolean true if the stream supports colorization, false otherwise
76849 */
76850 protected function hasColorSupport()
76851 {
76852 // @codeCoverageIgnoreStart
76853 if (DIRECTORY_SEPARATOR == '\\') {
76854 return false !== getenv('ANSICON') || 'ON' === getenv('ConEmuANSI');
76855 }
76856
76857 return function_exists('posix_isatty') && @posix_isatty($this->stream);
76858 // @codeCoverageIgnoreEnd
76859 }
76860 }
76861 <?php
76862
76863 /*
76864 * This file is part of the Symfony package.
76865 *
76866 * (c) Fabien Potencier <fabien@symfony.com>
76867 *
76868 * For the full copyright and license information, please view the LICENSE
76869 * file that was distributed with this source code.
76870 */
76871
76872 namespace Symfony\Component\Console\Output;
76873
76874 use Symfony\Component\Console\Formatter\OutputFormatterInterface;
76875 use Symfony\Component\Console\Output\ConsoleOutputInterface;
76876
76877 /**
76878 * ConsoleOutput is the default class for all CLI output. It uses STDOUT.
76879 *
76880 * This class is a convenient wrapper around `StreamOutput`.
76881 *
76882 * $output = new ConsoleOutput();
76883 *
76884 * This is equivalent to:
76885 *
76886 * $output = new StreamOutput(fopen('php://stdout', 'w'));
76887 *
76888 * @author Fabien Potencier <fabien@symfony.com>
76889 *
76890 * @api
76891 */
76892 class ConsoleOutput extends StreamOutput implements ConsoleOutputInterface
76893 {
76894 private $stderr;
76895
76896 /**
76897 * Constructor.
76898 *
76899 * @param integer $verbosity The verbosity level (one of the VERBOSITY constants in OutputInterface)
76900 * @param Boolean|null $decorated Whether to decorate messages (null for auto-guessing)
76901 * @param OutputFormatterInterface|null $formatter Output formatter instance (null to use default OutputFormatter)
76902 *
76903 * @api
76904 */
76905 public function __construct($verbosity = self::VERBOSITY_NORMAL, $decorated = null, OutputFormatterInterface $formatter = null)
76906 {
76907 $outputStream = 'php://stdout';
76908 if (!$this->hasStdoutSupport()) {
76909 $outputStream = 'php://output';
76910 }
76911
76912 parent::__construct(fopen($outputStream, 'w'), $verbosity, $decorated, $formatter);
76913
76914 $this->stderr = new StreamOutput(fopen('php://stderr', 'w'), $verbosity, $decorated, $formatter);
76915 }
76916
76917 /**
76918 * {@inheritdoc}
76919 */
76920 public function setDecorated($decorated)
76921 {
76922 parent::setDecorated($decorated);
76923 $this->stderr->setDecorated($decorated);
76924 }
76925
76926 /**
76927 * {@inheritdoc}
76928 */
76929 public function setFormatter(OutputFormatterInterface $formatter)
76930 {
76931 parent::setFormatter($formatter);
76932 $this->stderr->setFormatter($formatter);
76933 }
76934
76935 /**
76936 * {@inheritdoc}
76937 */
76938 public function setVerbosity($level)
76939 {
76940 parent::setVerbosity($level);
76941 $this->stderr->setVerbosity($level);
76942 }
76943
76944 /**
76945 * {@inheritdoc}
76946 */
76947 public function getErrorOutput()
76948 {
76949 return $this->stderr;
76950 }
76951
76952 /**
76953 * {@inheritdoc}
76954 */
76955 public function setErrorOutput(OutputInterface $error)
76956 {
76957 $this->stderr = $error;
76958 }
76959
76960 /**
76961 * Returns true if current environment supports writing console output to
76962 * STDOUT.
76963 *
76964 * IBM iSeries (OS400) exhibits character-encoding issues when writing to
76965 * STDOUT and doesn't properly convert ASCII to EBCDIC, resulting in garbage
76966 * output.
76967 *
76968 * @return boolean
76969 */
76970 protected function hasStdoutSupport()
76971 {
76972 return ('OS400' != php_uname('s'));
76973 }
76974 }
76975 <?php
76976
76977 /*
76978 * This file is part of the Symfony package.
76979 *
76980 * (c) Fabien Potencier <fabien@symfony.com>
76981 *
76982 * For the full copyright and license information, please view the LICENSE
76983 * file that was distributed with this source code.
76984 */
76985
76986 namespace Symfony\Component\Console\Output;
76987
76988 use Symfony\Component\Console\Formatter\OutputFormatterInterface;
76989
76990 /**
76991 * OutputInterface is the interface implemented by all Output classes.
76992 *
76993 * @author Fabien Potencier <fabien@symfony.com>
76994 *
76995 * @api
76996 */
76997 interface OutputInterface
76998 {
76999 const VERBOSITY_QUIET = 0;
77000 const VERBOSITY_NORMAL = 1;
77001 const VERBOSITY_VERBOSE = 2;
77002 const VERBOSITY_VERY_VERBOSE = 3;
77003 const VERBOSITY_DEBUG = 4;
77004
77005 const OUTPUT_NORMAL = 0;
77006 const OUTPUT_RAW = 1;
77007 const OUTPUT_PLAIN = 2;
77008
77009 /**
77010 * Writes a message to the output.
77011 *
77012 * @param string|array $messages The message as an array of lines or a single string
77013 * @param Boolean $newline Whether to add a newline
77014 * @param integer $type The type of output (one of the OUTPUT constants)
77015 *
77016 * @throws \InvalidArgumentException When unknown output type is given
77017 *
77018 * @api
77019 */
77020 public function write($messages, $newline = false, $type = self::OUTPUT_NORMAL);
77021
77022 /**
77023 * Writes a message to the output and adds a newline at the end.
77024 *
77025 * @param string|array $messages The message as an array of lines of a single string
77026 * @param integer $type The type of output (one of the OUTPUT constants)
77027 *
77028 * @throws \InvalidArgumentException When unknown output type is given
77029 *
77030 * @api
77031 */
77032 public function writeln($messages, $type = self::OUTPUT_NORMAL);
77033
77034 /**
77035 * Sets the verbosity of the output.
77036 *
77037 * @param integer $level The level of verbosity (one of the VERBOSITY constants)
77038 *
77039 * @api
77040 */
77041 public function setVerbosity($level);
77042
77043 /**
77044 * Gets the current verbosity of the output.
77045 *
77046 * @return integer The current level of verbosity (one of the VERBOSITY constants)
77047 *
77048 * @api
77049 */
77050 public function getVerbosity();
77051
77052 /**
77053 * Sets the decorated flag.
77054 *
77055 * @param Boolean $decorated Whether to decorate the messages
77056 *
77057 * @api
77058 */
77059 public function setDecorated($decorated);
77060
77061 /**
77062 * Gets the decorated flag.
77063 *
77064 * @return Boolean true if the output will decorate messages, false otherwise
77065 *
77066 * @api
77067 */
77068 public function isDecorated();
77069
77070 /**
77071 * Sets output formatter.
77072 *
77073 * @param OutputFormatterInterface $formatter
77074 *
77075 * @api
77076 */
77077 public function setFormatter(OutputFormatterInterface $formatter);
77078
77079 /**
77080 * Returns current output formatter instance.
77081 *
77082 * @return OutputFormatterInterface
77083 *
77084 * @api
77085 */
77086 public function getFormatter();
77087 }
77088 <?php
77089
77090 /*
77091 * This file is part of the Symfony package.
77092 *
77093 * (c) Fabien Potencier <fabien@symfony.com>
77094 *
77095 * For the full copyright and license information, please view the LICENSE
77096 * file that was distributed with this source code.
77097 */
77098
77099 namespace Symfony\Component\Console\Output;
77100
77101 use Symfony\Component\Console\Output\OutputInterface;
77102
77103 /**
77104 * ConsoleOutputInterface is the interface implemented by ConsoleOutput class.
77105 * This adds information about stderr output stream.
77106 *
77107 * @author Dariusz Górecki <darek.krk@gmail.com>
77108 */
77109 interface ConsoleOutputInterface extends OutputInterface
77110 {
77111 /**
77112 * Gets the OutputInterface for errors.
77113 *
77114 * @return OutputInterface
77115 */
77116 public function getErrorOutput();
77117
77118 /**
77119 * Sets the OutputInterface used for errors.
77120 *
77121 * @param OutputInterface $error
77122 */
77123 public function setErrorOutput(OutputInterface $error);
77124 }
77125 <?php
77126
77127 /*
77128 * This file is part of the Symfony package.
77129 *
77130 * (c) Fabien Potencier <fabien@symfony.com>
77131 *
77132 * For the full copyright and license information, please view the LICENSE
77133 * file that was distributed with this source code.
77134 */
77135
77136 namespace Symfony\Component\Console\Output;
77137
77138 use Symfony\Component\Console\Formatter\OutputFormatter;
77139 use Symfony\Component\Console\Formatter\OutputFormatterInterface;
77140
77141 /**
77142 * NullOutput suppresses all output.
77143 *
77144 * $output = new NullOutput();
77145 *
77146 * @author Fabien Potencier <fabien@symfony.com>
77147 * @author Tobias Schultze <http://tobion.de>
77148 *
77149 * @api
77150 */
77151 class NullOutput implements OutputInterface
77152 {
77153 /**
77154 * {@inheritdoc}
77155 */
77156 public function setFormatter(OutputFormatterInterface $formatter)
77157 {
77158 // do nothing
77159 }
77160
77161 /**
77162 * {@inheritdoc}
77163 */
77164 public function getFormatter()
77165 {
77166 // to comply with the interface we must return a OutputFormatterInterface
77167 return new OutputFormatter();
77168 }
77169
77170 /**
77171 * {@inheritdoc}
77172 */
77173 public function setDecorated($decorated)
77174 {
77175 // do nothing
77176 }
77177
77178 /**
77179 * {@inheritdoc}
77180 */
77181 public function isDecorated()
77182 {
77183 return false;
77184 }
77185
77186 /**
77187 * {@inheritdoc}
77188 */
77189 public function setVerbosity($level)
77190 {
77191 // do nothing
77192 }
77193
77194 /**
77195 * {@inheritdoc}
77196 */
77197 public function getVerbosity()
77198 {
77199 return self::VERBOSITY_QUIET;
77200 }
77201
77202 /**
77203 * {@inheritdoc}
77204 */
77205 public function writeln($messages, $type = self::OUTPUT_NORMAL)
77206 {
77207 // do nothing
77208 }
77209
77210 /**
77211 * {@inheritdoc}
77212 */
77213 public function write($messages, $newline = false, $type = self::OUTPUT_NORMAL)
77214 {
77215 // do nothing
77216 }
77217 }
77218 <?php
77219
77220 /*
77221 * This file is part of the Symfony package.
77222 *
77223 * (c) Fabien Potencier <fabien@symfony.com>
77224 *
77225 * For the full copyright and license information, please view the LICENSE
77226 * file that was distributed with this source code.
77227 */
77228
77229 namespace Symfony\Component\Console;
77230
77231 use Symfony\Component\Console\Descriptor\TextDescriptor;
77232 use Symfony\Component\Console\Descriptor\XmlDescriptor;
77233 use Symfony\Component\Console\Input\InputInterface;
77234 use Symfony\Component\Console\Input\ArgvInput;
77235 use Symfony\Component\Console\Input\ArrayInput;
77236 use Symfony\Component\Console\Input\InputDefinition;
77237 use Symfony\Component\Console\Input\InputOption;
77238 use Symfony\Component\Console\Input\InputArgument;
77239 use Symfony\Component\Console\Output\OutputInterface;
77240 use Symfony\Component\Console\Output\ConsoleOutput;
77241 use Symfony\Component\Console\Output\ConsoleOutputInterface;
77242 use Symfony\Component\Console\Command\Command;
77243 use Symfony\Component\Console\Command\HelpCommand;
77244 use Symfony\Component\Console\Command\ListCommand;
77245 use Symfony\Component\Console\Helper\HelperSet;
77246 use Symfony\Component\Console\Helper\FormatterHelper;
77247 use Symfony\Component\Console\Helper\DialogHelper;
77248 use Symfony\Component\Console\Helper\ProgressHelper;
77249 use Symfony\Component\Console\Helper\TableHelper;
77250 use Symfony\Component\Console\Event\ConsoleCommandEvent;
77251 use Symfony\Component\Console\Event\ConsoleExceptionEvent;
77252 use Symfony\Component\Console\Event\ConsoleTerminateEvent;
77253 use Symfony\Component\EventDispatcher\EventDispatcherInterface;
77254
77255 /**
77256 * An Application is the container for a collection of commands.
77257 *
77258 * It is the main entry point of a Console application.
77259 *
77260 * This class is optimized for a standard CLI environment.
77261 *
77262 * Usage:
77263 *
77264 * $app = new Application('myapp', '1.0 (stable)');
77265 * $app->add(new SimpleCommand());
77266 * $app->run();
77267 *
77268 * @author Fabien Potencier <fabien@symfony.com>
77269 *
77270 * @api
77271 */
77272 class Application
77273 {
77274 private $commands;
77275 private $wantHelps = false;
77276 private $runningCommand;
77277 private $name;
77278 private $version;
77279 private $catchExceptions;
77280 private $autoExit;
77281 private $definition;
77282 private $helperSet;
77283 private $dispatcher;
77284
77285 /**
77286 * Constructor.
77287 *
77288 * @param string $name The name of the application
77289 * @param string $version The version of the application
77290 *
77291 * @api
77292 */
77293 public function __construct($name = 'UNKNOWN', $version = 'UNKNOWN')
77294 {
77295 $this->name = $name;
77296 $this->version = $version;
77297 $this->catchExceptions = true;
77298 $this->autoExit = true;
77299 $this->commands = array();
77300 $this->helperSet = $this->getDefaultHelperSet();
77301 $this->definition = $this->getDefaultInputDefinition();
77302
77303 foreach ($this->getDefaultCommands() as $command) {
77304 $this->add($command);
77305 }
77306 }
77307
77308 public function setDispatcher(EventDispatcherInterface $dispatcher)
77309 {
77310 $this->dispatcher = $dispatcher;
77311 }
77312
77313 /**
77314 * Runs the current application.
77315 *
77316 * @param InputInterface $input An Input instance
77317 * @param OutputInterface $output An Output instance
77318 *
77319 * @return integer 0 if everything went fine, or an error code
77320 *
77321 * @throws \Exception When doRun returns Exception
77322 *
77323 * @api
77324 */
77325 public function run(InputInterface $input = null, OutputInterface $output = null)
77326 {
77327 if (null === $input) {
77328 $input = new ArgvInput();
77329 }
77330
77331 if (null === $output) {
77332 $output = new ConsoleOutput();
77333 }
77334
77335 $this->configureIO($input, $output);
77336
77337 try {
77338 $exitCode = $this->doRun($input, $output);
77339 } catch (\Exception $e) {
77340 if (!$this->catchExceptions) {
77341 throw $e;
77342 }
77343
77344 if ($output instanceof ConsoleOutputInterface) {
77345 $this->renderException($e, $output->getErrorOutput());
77346 } else {
77347 $this->renderException($e, $output);
77348 }
77349
77350 $exitCode = $e->getCode();
77351 if (is_numeric($exitCode)) {
77352 $exitCode = (int) $exitCode;
77353 if (0 === $exitCode) {
77354 $exitCode = 1;
77355 }
77356 } else {
77357 $exitCode = 1;
77358 }
77359 }
77360
77361 if ($this->autoExit) {
77362 if ($exitCode > 255) {
77363 $exitCode = 255;
77364 }
77365 // @codeCoverageIgnoreStart
77366 exit($exitCode);
77367 // @codeCoverageIgnoreEnd
77368 }
77369
77370 return $exitCode;
77371 }
77372
77373 /**
77374 * Runs the current application.
77375 *
77376 * @param InputInterface $input An Input instance
77377 * @param OutputInterface $output An Output instance
77378 *
77379 * @return integer 0 if everything went fine, or an error code
77380 */
77381 public function doRun(InputInterface $input, OutputInterface $output)
77382 {
77383 if (true === $input->hasParameterOption(array('--version', '-V'))) {
77384 $output->writeln($this->getLongVersion());
77385
77386 return 0;
77387 }
77388
77389 $name = $this->getCommandName($input);
77390 if (true === $input->hasParameterOption(array('--help', '-h'))) {
77391 if (!$name) {
77392 $name = 'help';
77393 $input = new ArrayInput(array('command' => 'help'));
77394 } else {
77395 $this->wantHelps = true;
77396 }
77397 }
77398
77399 if (!$name) {
77400 $name = 'list';
77401 $input = new ArrayInput(array('command' => 'list'));
77402 }
77403
77404 // the command name MUST be the first element of the input
77405 $command = $this->find($name);
77406
77407 $this->runningCommand = $command;
77408 $exitCode = $this->doRunCommand($command, $input, $output);
77409 $this->runningCommand = null;
77410
77411 return $exitCode;
77412 }
77413
77414 /**
77415 * Set a helper set to be used with the command.
77416 *
77417 * @param HelperSet $helperSet The helper set
77418 *
77419 * @api
77420 */
77421 public function setHelperSet(HelperSet $helperSet)
77422 {
77423 $this->helperSet = $helperSet;
77424 }
77425
77426 /**
77427 * Get the helper set associated with the command.
77428 *
77429 * @return HelperSet The HelperSet instance associated with this command
77430 *
77431 * @api
77432 */
77433 public function getHelperSet()
77434 {
77435 return $this->helperSet;
77436 }
77437
77438 /**
77439 * Set an input definition set to be used with this application
77440 *
77441 * @param InputDefinition $definition The input definition
77442 *
77443 * @api
77444 */
77445 public function setDefinition(InputDefinition $definition)
77446 {
77447 $this->definition = $definition;
77448 }
77449
77450 /**
77451 * Gets the InputDefinition related to this Application.
77452 *
77453 * @return InputDefinition The InputDefinition instance
77454 */
77455 public function getDefinition()
77456 {
77457 return $this->definition;
77458 }
77459
77460 /**
77461 * Gets the help message.
77462 *
77463 * @return string A help message.
77464 */
77465 public function getHelp()
77466 {
77467 $messages = array(
77468 $this->getLongVersion(),
77469 '',
77470 '<comment>Usage:</comment>',
77471 ' [options] command [arguments]',
77472 '',
77473 '<comment>Options:</comment>',
77474 );
77475
77476 foreach ($this->getDefinition()->getOptions() as $option) {
77477 $messages[] = sprintf(' %-29s %s %s',
77478 '<info>--'.$option->getName().'</info>',
77479 $option->getShortcut() ? '<info>-'.$option->getShortcut().'</info>' : ' ',
77480 $option->getDescription()
77481 );
77482 }
77483
77484 return implode(PHP_EOL, $messages);
77485 }
77486
77487 /**
77488 * Sets whether to catch exceptions or not during commands execution.
77489 *
77490 * @param Boolean $boolean Whether to catch exceptions or not during commands execution
77491 *
77492 * @api
77493 */
77494 public function setCatchExceptions($boolean)
77495 {
77496 $this->catchExceptions = (Boolean) $boolean;
77497 }
77498
77499 /**
77500 * Sets whether to automatically exit after a command execution or not.
77501 *
77502 * @param Boolean $boolean Whether to automatically exit after a command execution or not
77503 *
77504 * @api
77505 */
77506 public function setAutoExit($boolean)
77507 {
77508 $this->autoExit = (Boolean) $boolean;
77509 }
77510
77511 /**
77512 * Gets the name of the application.
77513 *
77514 * @return string The application name
77515 *
77516 * @api
77517 */
77518 public function getName()
77519 {
77520 return $this->name;
77521 }
77522
77523 /**
77524 * Sets the application name.
77525 *
77526 * @param string $name The application name
77527 *
77528 * @api
77529 */
77530 public function setName($name)
77531 {
77532 $this->name = $name;
77533 }
77534
77535 /**
77536 * Gets the application version.
77537 *
77538 * @return string The application version
77539 *
77540 * @api
77541 */
77542 public function getVersion()
77543 {
77544 return $this->version;
77545 }
77546
77547 /**
77548 * Sets the application version.
77549 *
77550 * @param string $version The application version
77551 *
77552 * @api
77553 */
77554 public function setVersion($version)
77555 {
77556 $this->version = $version;
77557 }
77558
77559 /**
77560 * Returns the long version of the application.
77561 *
77562 * @return string The long application version
77563 *
77564 * @api
77565 */
77566 public function getLongVersion()
77567 {
77568 if ('UNKNOWN' !== $this->getName() && 'UNKNOWN' !== $this->getVersion()) {
77569 return sprintf('<info>%s</info> version <comment>%s</comment>', $this->getName(), $this->getVersion());
77570 }
77571
77572 return '<info>Console Tool</info>';
77573 }
77574
77575 /**
77576 * Registers a new command.
77577 *
77578 * @param string $name The command name
77579 *
77580 * @return Command The newly created command
77581 *
77582 * @api
77583 */
77584 public function register($name)
77585 {
77586 return $this->add(new Command($name));
77587 }
77588
77589 /**
77590 * Adds an array of command objects.
77591 *
77592 * @param Command[] $commands An array of commands
77593 *
77594 * @api
77595 */
77596 public function addCommands(array $commands)
77597 {
77598 foreach ($commands as $command) {
77599 $this->add($command);
77600 }
77601 }
77602
77603 /**
77604 * Adds a command object.
77605 *
77606 * If a command with the same name already exists, it will be overridden.
77607 *
77608 * @param Command $command A Command object
77609 *
77610 * @return Command The registered command
77611 *
77612 * @api
77613 */
77614 public function add(Command $command)
77615 {
77616 $command->setApplication($this);
77617
77618 if (!$command->isEnabled()) {
77619 $command->setApplication(null);
77620
77621 return;
77622 }
77623
77624 $this->commands[$command->getName()] = $command;
77625
77626 foreach ($command->getAliases() as $alias) {
77627 $this->commands[$alias] = $command;
77628 }
77629
77630 return $command;
77631 }
77632
77633 /**
77634 * Returns a registered command by name or alias.
77635 *
77636 * @param string $name The command name or alias
77637 *
77638 * @return Command A Command object
77639 *
77640 * @throws \InvalidArgumentException When command name given does not exist
77641 *
77642 * @api
77643 */
77644 public function get($name)
77645 {
77646 if (!isset($this->commands[$name])) {
77647 throw new \InvalidArgumentException(sprintf('The command "%s" does not exist.', $name));
77648 }
77649
77650 $command = $this->commands[$name];
77651
77652 if ($this->wantHelps) {
77653 $this->wantHelps = false;
77654
77655 $helpCommand = $this->get('help');
77656 $helpCommand->setCommand($command);
77657
77658 return $helpCommand;
77659 }
77660
77661 return $command;
77662 }
77663
77664 /**
77665 * Returns true if the command exists, false otherwise.
77666 *
77667 * @param string $name The command name or alias
77668 *
77669 * @return Boolean true if the command exists, false otherwise
77670 *
77671 * @api
77672 */
77673 public function has($name)
77674 {
77675 return isset($this->commands[$name]);
77676 }
77677
77678 /**
77679 * Returns an array of all unique namespaces used by currently registered commands.
77680 *
77681 * It does not returns the global namespace which always exists.
77682 *
77683 * @return array An array of namespaces
77684 */
77685 public function getNamespaces()
77686 {
77687 $namespaces = array();
77688 foreach ($this->commands as $command) {
77689 $namespaces[] = $this->extractNamespace($command->getName());
77690
77691 foreach ($command->getAliases() as $alias) {
77692 $namespaces[] = $this->extractNamespace($alias);
77693 }
77694 }
77695
77696 return array_values(array_unique(array_filter($namespaces)));
77697 }
77698
77699 /**
77700 * Finds a registered namespace by a name or an abbreviation.
77701 *
77702 * @param string $namespace A namespace or abbreviation to search for
77703 *
77704 * @return string A registered namespace
77705 *
77706 * @throws \InvalidArgumentException When namespace is incorrect or ambiguous
77707 */
77708 public function findNamespace($namespace)
77709 {
77710 $allNamespaces = $this->getNamespaces();
77711 $found = '';
77712 foreach (explode(':', $namespace) as $i => $part) {
77713 // select sub-namespaces matching the current namespace we found
77714 $namespaces = array();
77715 foreach ($allNamespaces as $n) {
77716 if ('' === $found || 0 === strpos($n, $found)) {
77717 $namespaces[$n] = explode(':', $n);
77718 }
77719 }
77720
77721 $abbrevs = static::getAbbreviations(array_unique(array_values(array_filter(array_map(function ($p) use ($i) { return isset($p[$i]) ? $p[$i] : ''; }, $namespaces)))));
77722
77723 if (!isset($abbrevs[$part])) {
77724 $message = sprintf('There are no commands defined in the "%s" namespace.', $namespace);
77725
77726 if (1 <= $i) {
77727 $part = $found.':'.$part;
77728 }
77729
77730 if ($alternatives = $this->findAlternativeNamespace($part, $abbrevs)) {
77731 if (1 == count($alternatives)) {
77732 $message .= "\n\nDid you mean this?\n ";
77733 } else {
77734 $message .= "\n\nDid you mean one of these?\n ";
77735 }
77736
77737 $message .= implode("\n ", $alternatives);
77738 }
77739
77740 throw new \InvalidArgumentException($message);
77741 }
77742
77743 // there are multiple matches, but $part is an exact match of one of them so we select it
77744 if (in_array($part, $abbrevs[$part])) {
77745 $abbrevs[$part] = array($part);
77746 }
77747
77748 if (count($abbrevs[$part]) > 1) {
77749 throw new \InvalidArgumentException(sprintf('The namespace "%s" is ambiguous (%s).', $namespace, $this->getAbbreviationSuggestions($abbrevs[$part])));
77750 }
77751
77752 $found .= $found ? ':' . $abbrevs[$part][0] : $abbrevs[$part][0];
77753 }
77754
77755 return $found;
77756 }
77757
77758 /**
77759 * Finds a command by name or alias.
77760 *
77761 * Contrary to get, this command tries to find the best
77762 * match if you give it an abbreviation of a name or alias.
77763 *
77764 * @param string $name A command name or a command alias
77765 *
77766 * @return Command A Command instance
77767 *
77768 * @throws \InvalidArgumentException When command name is incorrect or ambiguous
77769 *
77770 * @api
77771 */
77772 public function find($name)
77773 {
77774 // namespace
77775 $namespace = '';
77776 $searchName = $name;
77777 if (false !== $pos = strrpos($name, ':')) {
77778 $namespace = $this->findNamespace(substr($name, 0, $pos));
77779 $searchName = $namespace.substr($name, $pos);
77780 }
77781
77782 // name
77783 $commands = array();
77784 foreach ($this->commands as $command) {
77785 $extractedNamespace = $this->extractNamespace($command->getName());
77786 if ($extractedNamespace === $namespace
77787 || !empty($namespace) && 0 === strpos($extractedNamespace, $namespace)
77788 ) {
77789 $commands[] = $command->getName();
77790 }
77791 }
77792
77793 $abbrevs = static::getAbbreviations(array_unique($commands));
77794 if (isset($abbrevs[$searchName]) && 1 == count($abbrevs[$searchName])) {
77795 return $this->get($abbrevs[$searchName][0]);
77796 }
77797
77798 if (isset($abbrevs[$searchName]) && in_array($searchName, $abbrevs[$searchName])) {
77799 return $this->get($searchName);
77800 }
77801
77802 if (isset($abbrevs[$searchName]) && count($abbrevs[$searchName]) > 1) {
77803 $suggestions = $this->getAbbreviationSuggestions($abbrevs[$searchName]);
77804
77805 throw new \InvalidArgumentException(sprintf('Command "%s" is ambiguous (%s).', $name, $suggestions));
77806 }
77807
77808 // aliases
77809 $aliases = array();
77810 foreach ($this->commands as $command) {
77811 foreach ($command->getAliases() as $alias) {
77812 $extractedNamespace = $this->extractNamespace($alias);
77813 if ($extractedNamespace === $namespace
77814 || !empty($namespace) && 0 === strpos($extractedNamespace, $namespace)
77815 ) {
77816 $aliases[] = $alias;
77817 }
77818 }
77819 }
77820
77821 $aliases = static::getAbbreviations(array_unique($aliases));
77822 if (!isset($aliases[$searchName])) {
77823 $message = sprintf('Command "%s" is not defined.', $name);
77824
77825 if ($alternatives = $this->findAlternativeCommands($searchName, $abbrevs)) {
77826 if (1 == count($alternatives)) {
77827 $message .= "\n\nDid you mean this?\n ";
77828 } else {
77829 $message .= "\n\nDid you mean one of these?\n ";
77830 }
77831 $message .= implode("\n ", $alternatives);
77832 }
77833
77834 throw new \InvalidArgumentException($message);
77835 }
77836
77837 if (count($aliases[$searchName]) > 1) {
77838 throw new \InvalidArgumentException(sprintf('Command "%s" is ambiguous (%s).', $name, $this->getAbbreviationSuggestions($aliases[$searchName])));
77839 }
77840
77841 return $this->get($aliases[$searchName][0]);
77842 }
77843
77844 /**
77845 * Gets the commands (registered in the given namespace if provided).
77846 *
77847 * The array keys are the full names and the values the command instances.
77848 *
77849 * @param string $namespace A namespace name
77850 *
77851 * @return Command[] An array of Command instances
77852 *
77853 * @api
77854 */
77855 public function all($namespace = null)
77856 {
77857 if (null === $namespace) {
77858 return $this->commands;
77859 }
77860
77861 $commands = array();
77862 foreach ($this->commands as $name => $command) {
77863 if ($namespace === $this->extractNamespace($name, substr_count($namespace, ':') + 1)) {
77864 $commands[$name] = $command;
77865 }
77866 }
77867
77868 return $commands;
77869 }
77870
77871 /**
77872 * Returns an array of possible abbreviations given a set of names.
77873 *
77874 * @param array $names An array of names
77875 *
77876 * @return array An array of abbreviations
77877 */
77878 public static function getAbbreviations($names)
77879 {
77880 $abbrevs = array();
77881 foreach ($names as $name) {
77882 for ($len = strlen($name); $len > 0; --$len) {
77883 $abbrev = substr($name, 0, $len);
77884 $abbrevs[$abbrev][] = $name;
77885 }
77886 }
77887
77888 return $abbrevs;
77889 }
77890
77891 /**
77892 * Returns a text representation of the Application.
77893 *
77894 * @param string $namespace An optional namespace name
77895 * @param boolean $raw Whether to return raw command list
77896 *
77897 * @return string A string representing the Application
77898 *
77899 * @deprecated Deprecated since version 2.3, to be removed in 3.0.
77900 */
77901 public function asText($namespace = null, $raw = false)
77902 {
77903 $descriptor = new TextDescriptor();
77904
77905 return $descriptor->describe($this, array('namespace' => $namespace, 'raw_text' => $raw));
77906 }
77907
77908 /**
77909 * Returns an XML representation of the Application.
77910 *
77911 * @param string $namespace An optional namespace name
77912 * @param Boolean $asDom Whether to return a DOM or an XML string
77913 *
77914 * @return string|\DOMDocument An XML string representing the Application
77915 *
77916 * @deprecated Deprecated since version 2.3, to be removed in 3.0.
77917 */
77918 public function asXml($namespace = null, $asDom = false)
77919 {
77920 $descriptor = new XmlDescriptor();
77921
77922 return $descriptor->describe($this, array('namespace' => $namespace, 'as_dom' => $asDom));
77923 }
77924
77925 /**
77926 * Renders a caught exception.
77927 *
77928 * @param Exception $e An exception instance
77929 * @param OutputInterface $output An OutputInterface instance
77930 */
77931 public function renderException($e, $output)
77932 {
77933 $strlen = function ($string) {
77934 if (!function_exists('mb_strlen')) {
77935 return strlen($string);
77936 }
77937
77938 if (false === $encoding = mb_detect_encoding($string)) {
77939 return strlen($string);
77940 }
77941
77942 return mb_strlen($string, $encoding);
77943 };
77944
77945 do {
77946 $title = sprintf(' [%s] ', get_class($e));
77947 $len = $strlen($title);
77948 $width = $this->getTerminalWidth() ? $this->getTerminalWidth() - 1 : PHP_INT_MAX;
77949 $lines = array();
77950 foreach (preg_split('/\r?\n/', $e->getMessage()) as $line) {
77951 foreach (str_split($line, $width - 4) as $line) {
77952 $lines[] = sprintf(' %s ', $line);
77953 $len = max($strlen($line) + 4, $len);
77954 }
77955 }
77956
77957 $messages = array(str_repeat(' ', $len), $title.str_repeat(' ', max(0, $len - $strlen($title))));
77958
77959 foreach ($lines as $line) {
77960 $messages[] = $line.str_repeat(' ', $len - $strlen($line));
77961 }
77962
77963 $messages[] = str_repeat(' ', $len);
77964
77965 $output->writeln("");
77966 $output->writeln("");
77967 foreach ($messages as $message) {
77968 $output->writeln('<error>'.$message.'</error>');
77969 }
77970 $output->writeln("");
77971 $output->writeln("");
77972
77973 if (OutputInterface::VERBOSITY_VERBOSE <= $output->getVerbosity()) {
77974 $output->writeln('<comment>Exception trace:</comment>');
77975
77976 // exception related properties
77977 $trace = $e->getTrace();
77978 array_unshift($trace, array(
77979 'function' => '',
77980 'file' => $e->getFile() != null ? $e->getFile() : 'n/a',
77981 'line' => $e->getLine() != null ? $e->getLine() : 'n/a',
77982 'args' => array(),
77983 ));
77984
77985 for ($i = 0, $count = count($trace); $i < $count; $i++) {
77986 $class = isset($trace[$i]['class']) ? $trace[$i]['class'] : '';
77987 $type = isset($trace[$i]['type']) ? $trace[$i]['type'] : '';
77988 $function = $trace[$i]['function'];
77989 $file = isset($trace[$i]['file']) ? $trace[$i]['file'] : 'n/a';
77990 $line = isset($trace[$i]['line']) ? $trace[$i]['line'] : 'n/a';
77991
77992 $output->writeln(sprintf(' %s%s%s() at <info>%s:%s</info>', $class, $type, $function, $file, $line));
77993 }
77994
77995 $output->writeln("");
77996 $output->writeln("");
77997 }
77998 } while ($e = $e->getPrevious());
77999
78000 if (null !== $this->runningCommand) {
78001 $output->writeln(sprintf('<info>%s</info>', sprintf($this->runningCommand->getSynopsis(), $this->getName())));
78002 $output->writeln("");
78003 $output->writeln("");
78004 }
78005 }
78006
78007 /**
78008 * Tries to figure out the terminal width in which this application runs
78009 *
78010 * @return int|null
78011 */
78012 protected function getTerminalWidth()
78013 {
78014 $dimensions = $this->getTerminalDimensions();
78015
78016 return $dimensions[0];
78017 }
78018
78019 /**
78020 * Tries to figure out the terminal height in which this application runs
78021 *
78022 * @return int|null
78023 */
78024 protected function getTerminalHeight()
78025 {
78026 $dimensions = $this->getTerminalDimensions();
78027
78028 return $dimensions[1];
78029 }
78030
78031 /**
78032 * Tries to figure out the terminal dimensions based on the current environment
78033 *
78034 * @return array Array containing width and height
78035 */
78036 public function getTerminalDimensions()
78037 {
78038 if (defined('PHP_WINDOWS_VERSION_BUILD')) {
78039 // extract [w, H] from "wxh (WxH)"
78040 if (preg_match('/^(\d+)x\d+ \(\d+x(\d+)\)$/', trim(getenv('ANSICON')), $matches)) {
78041 return array((int) $matches[1], (int) $matches[2]);
78042 }
78043 // extract [w, h] from "wxh"
78044 if (preg_match('/^(\d+)x(\d+)$/', $this->getConsoleMode(), $matches)) {
78045 return array((int) $matches[1], (int) $matches[2]);
78046 }
78047 }
78048
78049 if ($sttyString = $this->getSttyColumns()) {
78050 // extract [w, h] from "rows h; columns w;"
78051 if (preg_match('/rows.(\d+);.columns.(\d+);/i', $sttyString, $matches)) {
78052 return array((int) $matches[2], (int) $matches[1]);
78053 }
78054 // extract [w, h] from "; h rows; w columns"
78055 if (preg_match('/;.(\d+).rows;.(\d+).columns/i', $sttyString, $matches)) {
78056 return array((int) $matches[2], (int) $matches[1]);
78057 }
78058 }
78059
78060 return array(null, null);
78061 }
78062
78063 /**
78064 * Configures the input and output instances based on the user arguments and options.
78065 *
78066 * @param InputInterface $input An InputInterface instance
78067 * @param OutputInterface $output An OutputInterface instance
78068 */
78069 protected function configureIO(InputInterface $input, OutputInterface $output)
78070 {
78071 if (true === $input->hasParameterOption(array('--ansi'))) {
78072 $output->setDecorated(true);
78073 } elseif (true === $input->hasParameterOption(array('--no-ansi'))) {
78074 $output->setDecorated(false);
78075 }
78076
78077 if (true === $input->hasParameterOption(array('--no-interaction', '-n'))) {
78078 $input->setInteractive(false);
78079 } elseif (function_exists('posix_isatty') && $this->getHelperSet()->has('dialog')) {
78080 $inputStream = $this->getHelperSet()->get('dialog')->getInputStream();
78081 if (!posix_isatty($inputStream)) {
78082 $input->setInteractive(false);
78083 }
78084 }
78085
78086 if (true === $input->hasParameterOption(array('--quiet', '-q'))) {
78087 $output->setVerbosity(OutputInterface::VERBOSITY_QUIET);
78088 } else {
78089 if ($input->hasParameterOption('-vvv') || $input->hasParameterOption('--verbose=3') || $input->getParameterOption('--verbose') === 3) {
78090 $output->setVerbosity(OutputInterface::VERBOSITY_DEBUG);
78091 } elseif ($input->hasParameterOption('-vv') || $input->hasParameterOption('--verbose=2') || $input->getParameterOption('--verbose') === 2) {
78092 $output->setVerbosity(OutputInterface::VERBOSITY_VERY_VERBOSE);
78093 } elseif ($input->hasParameterOption('-v') || $input->hasParameterOption('--verbose=1') || $input->hasParameterOption('--verbose') || $input->getParameterOption('--verbose')) {
78094 $output->setVerbosity(OutputInterface::VERBOSITY_VERBOSE);
78095 }
78096 }
78097 }
78098
78099 /**
78100 * Runs the current command.
78101 *
78102 * If an event dispatcher has been attached to the application,
78103 * events are also dispatched during the life-cycle of the command.
78104 *
78105 * @param Command $command A Command instance
78106 * @param InputInterface $input An Input instance
78107 * @param OutputInterface $output An Output instance
78108 *
78109 * @return integer 0 if everything went fine, or an error code
78110 */
78111 protected function doRunCommand(Command $command, InputInterface $input, OutputInterface $output)
78112 {
78113 if (null === $this->dispatcher) {
78114 return $command->run($input, $output);
78115 }
78116
78117 $event = new ConsoleCommandEvent($command, $input, $output);
78118 $this->dispatcher->dispatch(ConsoleEvents::COMMAND, $event);
78119
78120 try {
78121 $exitCode = $command->run($input, $output);
78122 } catch (\Exception $e) {
78123 $event = new ConsoleTerminateEvent($command, $input, $output, $e->getCode());
78124 $this->dispatcher->dispatch(ConsoleEvents::TERMINATE, $event);
78125
78126 $event = new ConsoleExceptionEvent($command, $input, $output, $e, $event->getExitCode());
78127 $this->dispatcher->dispatch(ConsoleEvents::EXCEPTION, $event);
78128
78129 throw $event->getException();
78130 }
78131
78132 $event = new ConsoleTerminateEvent($command, $input, $output, $exitCode);
78133 $this->dispatcher->dispatch(ConsoleEvents::TERMINATE, $event);
78134
78135 return $event->getExitCode();
78136 }
78137
78138 /**
78139 * Gets the name of the command based on input.
78140 *
78141 * @param InputInterface $input The input interface
78142 *
78143 * @return string The command name
78144 */
78145 protected function getCommandName(InputInterface $input)
78146 {
78147 return $input->getFirstArgument();
78148 }
78149
78150 /**
78151 * Gets the default input definition.
78152 *
78153 * @return InputDefinition An InputDefinition instance
78154 */
78155 protected function getDefaultInputDefinition()
78156 {
78157 return new InputDefinition(array(
78158 new InputArgument('command', InputArgument::REQUIRED, 'The command to execute'),
78159
78160 new InputOption('--help', '-h', InputOption::VALUE_NONE, 'Display this help message.'),
78161 new InputOption('--quiet', '-q', InputOption::VALUE_NONE, 'Do not output any message.'),
78162 new InputOption('--verbose', '-v|vv|vvv', InputOption::VALUE_NONE, 'Increase the verbosity of messages: 1 for normal output, 2 for more verbose output and 3 for debug'),
78163 new InputOption('--version', '-V', InputOption::VALUE_NONE, 'Display this application version.'),
78164 new InputOption('--ansi', '', InputOption::VALUE_NONE, 'Force ANSI output.'),
78165 new InputOption('--no-ansi', '', InputOption::VALUE_NONE, 'Disable ANSI output.'),
78166 new InputOption('--no-interaction', '-n', InputOption::VALUE_NONE, 'Do not ask any interactive question.'),
78167 ));
78168 }
78169
78170 /**
78171 * Gets the default commands that should always be available.
78172 *
78173 * @return Command[] An array of default Command instances
78174 */
78175 protected function getDefaultCommands()
78176 {
78177 return array(new HelpCommand(), new ListCommand());
78178 }
78179
78180 /**
78181 * Gets the default helper set with the helpers that should always be available.
78182 *
78183 * @return HelperSet A HelperSet instance
78184 */
78185 protected function getDefaultHelperSet()
78186 {
78187 return new HelperSet(array(
78188 new FormatterHelper(),
78189 new DialogHelper(),
78190 new ProgressHelper(),
78191 new TableHelper(),
78192 ));
78193 }
78194
78195 /**
78196 * Runs and parses stty -a if it's available, suppressing any error output
78197 *
78198 * @return string
78199 */
78200 private function getSttyColumns()
78201 {
78202 if (!function_exists('proc_open')) {
78203 return;
78204 }
78205
78206 $descriptorspec = array(1 => array('pipe', 'w'), 2 => array('pipe', 'w'));
78207 $process = proc_open('stty -a | grep columns', $descriptorspec, $pipes, null, null, array('suppress_errors' => true));
78208 if (is_resource($process)) {
78209 $info = stream_get_contents($pipes[1]);
78210 fclose($pipes[1]);
78211 fclose($pipes[2]);
78212 proc_close($process);
78213
78214 return $info;
78215 }
78216 }
78217
78218 /**
78219 * Runs and parses mode CON if it's available, suppressing any error output
78220 *
78221 * @return string <width>x<height> or null if it could not be parsed
78222 */
78223 private function getConsoleMode()
78224 {
78225 if (!function_exists('proc_open')) {
78226 return;
78227 }
78228
78229 $descriptorspec = array(1 => array('pipe', 'w'), 2 => array('pipe', 'w'));
78230 $process = proc_open('mode CON', $descriptorspec, $pipes, null, null, array('suppress_errors' => true));
78231 if (is_resource($process)) {
78232 $info = stream_get_contents($pipes[1]);
78233 fclose($pipes[1]);
78234 fclose($pipes[2]);
78235 proc_close($process);
78236
78237 if (preg_match('/--------+\r?\n.+?(\d+)\r?\n.+?(\d+)\r?\n/', $info, $matches)) {
78238 return $matches[2].'x'.$matches[1];
78239 }
78240 }
78241 }
78242
78243 /**
78244 * Returns abbreviated suggestions in string format.
78245 *
78246 * @param array $abbrevs Abbreviated suggestions to convert
78247 *
78248 * @return string A formatted string of abbreviated suggestions
78249 */
78250 private function getAbbreviationSuggestions($abbrevs)
78251 {
78252 return sprintf('%s, %s%s', $abbrevs[0], $abbrevs[1], count($abbrevs) > 2 ? sprintf(' and %d more', count($abbrevs) - 2) : '');
78253 }
78254
78255 /**
78256 * Returns the namespace part of the command name.
78257 *
78258 * This method is not part of public API and should not be used directly.
78259 *
78260 * @param string $name The full name of the command
78261 * @param string $limit The maximum number of parts of the namespace
78262 *
78263 * @return string The namespace of the command
78264 */
78265 public function extractNamespace($name, $limit = null)
78266 {
78267 $parts = explode(':', $name);
78268 array_pop($parts);
78269
78270 return implode(':', null === $limit ? $parts : array_slice($parts, 0, $limit));
78271 }
78272
78273 /**
78274 * Finds alternative commands of $name
78275 *
78276 * @param string $name The full name of the command
78277 * @param array $abbrevs The abbreviations
78278 *
78279 * @return array A sorted array of similar commands
78280 */
78281 private function findAlternativeCommands($name, $abbrevs)
78282 {
78283 $callback = function($item) {
78284 return $item->getName();
78285 };
78286
78287 return $this->findAlternatives($name, $this->commands, $abbrevs, $callback);
78288 }
78289
78290 /**
78291 * Finds alternative namespace of $name
78292 *
78293 * @param string $name The full name of the namespace
78294 * @param array $abbrevs The abbreviations
78295 *
78296 * @return array A sorted array of similar namespace
78297 */
78298 private function findAlternativeNamespace($name, $abbrevs)
78299 {
78300 return $this->findAlternatives($name, $this->getNamespaces(), $abbrevs);
78301 }
78302
78303 /**
78304 * Finds alternative of $name among $collection,
78305 * if nothing is found in $collection, try in $abbrevs
78306 *
78307 * @param string $name The string
78308 * @param array|Traversable $collection The collection
78309 * @param array $abbrevs The abbreviations
78310 * @param Closure|string|array $callback The callable to transform collection item before comparison
78311 *
78312 * @return array A sorted array of similar string
78313 */
78314 private function findAlternatives($name, $collection, $abbrevs, $callback = null)
78315 {
78316 $alternatives = array();
78317
78318 foreach ($collection as $item) {
78319 if (null !== $callback) {
78320 $item = call_user_func($callback, $item);
78321 }
78322
78323 $lev = levenshtein($name, $item);
78324 if ($lev <= strlen($name) / 3 || false !== strpos($item, $name)) {
78325 $alternatives[$item] = $lev;
78326 }
78327 }
78328
78329 if (!$alternatives) {
78330 foreach ($abbrevs as $key => $values) {
78331 $lev = levenshtein($name, $key);
78332 if ($lev <= strlen($name) / 3 || false !== strpos($key, $name)) {
78333 foreach ($values as $value) {
78334 $alternatives[$value] = $lev;
78335 }
78336 }
78337 }
78338 }
78339
78340 asort($alternatives);
78341
78342 return array_keys($alternatives);
78343 }
78344 }
78345 MZ����@��� �!�L�!This program cannot be run in DOS mode.
78346 $�,�;�B�;�B�;�B�2�מ:�B�2��-�B�2�ƞ9�B�2�ў?�B�a9�8�B�;�C��B�2�Ȟ:�B�2�֞:�B�2�Ӟ:�B�Rich;�B�PEL�MoO� 
78347 8 @`?�@��"P@ Pp!8!@ �.text 
78348  `.rdata�
78349 @@.data�0@�.rsrc @@@.reloc�P"@Bj$��@�xj�� @�e���E�PV� @�EЃ�PV� @�M�X @�e��E�P�5H @�L @YY�5\ @�E�P�5` @�D @YY��P @�M���M�T @3��H�; 0@u���h�@��l3@�$40@�5h3@�40@h$0@h(0@h 0@�� @���00@��}j�Y�jh"@�3ۉ]�d��p�]俀3@SVW�0 @;�t;�u3�F�u��h��4 @��3�F�|3@;�u
78350 j�\Y�;�|3@��u,�5|3@h� @h� @�YY��t�E����������5<0@�|3@;�uh� @h� @�lYY�|3@9]�uSW�8 @9�3@th�3@�Y��t
78351 SjS��3@�$0@� � @��5$0@�5(0@�5 0@������ �80@9,0@u7P�� @�E�� �M�PQ�YYËe�E�80@3�9,0@uP�h @9<0@u�� @�E������80@�øMZf9@t3��M�<@��@�8PEu��H�� t�� uՃ��v�3�9����xtv�3�9������j�,0@�p @j��l @YY��3@��3@�� @� t3@��� @� p3@��� @��x3@�V��=0@u h�@�� @Y�g�=0@�u j��� @Y3���{�����U���(�H1@� D1@�@1@�<1@�581@�=41@f�`1@f� T1@f�01@f�,1@f�%(1@f�-$1@��X1@�E�L1@�E�P1@�E�\1@������0@�P1@�L0@�@0@ ��D0@�0@������0@������ @��0@j�?Yj� @h!@�$ @�=�0@uj�Yh ��( @P�, @�Ë�U��E��8csm�u*�xu$�@= �t=!�t="�t=@�u��3�]�hH@� @3��%� @jh("@�b�5�3@�5� @��Y�E��u �u�� @Y�gj�Y�e��5�3@�։E�5�3@��YY�E�E�P�E�P�u�5l @��YP�U�E�u�֣�3@�u�փ���3@�E������ �E���j�YË�U��u�N��������YH]Ë�V��!@��!@W��;�s���t�Ѓ�;�r�_^Ë�V�"@�"@W��;�s���t�Ѓ�;�r�_^�%� @���̋�U��M�MZf9t3�]ËA<��8PEu�3ҹ f9H�‹�]�����������̋�U��E�H<��ASV�q3�W�D��v�} �H ;�r �X�;�r
78352 B��(;�r�3�_^[]������������̋�U��j�hH"@he@d�P��SVW�0@1E�3�P�E�d��e��E�h@�*�������tU�E-@Ph@�P�������t;�@$���Ѓ��E������M�d� Y_^[��]ËE��3�=��‹�Ëe��E�����3��M�d� Y_^[��]��%� @�%� @��he@d�5�D$�l$�l$+�SVW�0@1E�3�P�e�u��E��E������E��E�d�ËM�d� Y__^[��]QË�U��u�u�u �uh�@h0@����]Ë�Vhh3�V���� ��t VVVVV����^�3�Ë�U����0@�e��e�SW�N�@����;�t ��t �У0@�`V�E�P�< @�u�3u�� @3� @3� @3�E�P� @�E�3E�3�;�u�O�@�� ��u��� �50@�։50@^_[��%t @�%x @�%| @�%� @�%� @�%� @�%� @�%� @�%� @Pd�5�D$ +d$ SVW�(��0@3�P�E�u��E������E�d�ËM�d� Y__^[��]QËM�3���������M�%T @�T$�B �J�3�����J�3�����l"@�s����#�#�#�)r)b)H)4))�(�(�(�(�(�(�)�#�$%�%&d&�&�$('�'�'�'�'(((6(�'H(Z(t(�('''�'�'l'^'R'F'>'>(0'�'�)�@W@�@�MoOl�!�@0@�0@bad allocationH0@�!@RSDSь���J�!���LZc:\users\seld\documents\visual studio 2010\Projects\hiddeninp\Release\hiddeninp.pdbe������������@@�����������:@������������@�@�����@"�d"@�"�# $#�&D H#(h �#�#�#�)r)b)H)4))�(�(�(�(�(�(�)�#�$%�%&d&�&�$('�'�'�'�'(((6(�'H(Z(t(�('''�'�'l'^'R'F'>'>(0'�'�)�GetConsoleMode�SetConsoleMode;GetStdHandleKERNEL32.dll??$?6DU?$char_traits@D@std@@V?$allocator@D@1@@std@@YAAAV?$basic_ostream@DU?$char_traits@D@std@@@0@AAV10@ABV?$basic_string@DU?$char_traits@D@std@@V?$allocator@D@2@@0@@Z�?cout@std@@3V?$basic_ostream@DU?$char_traits@D@std@@@1@AJ?cin@std@@3V?$basic_istream@DU?$char_traits@D@std@@@1@A�??$getline@DU?$char_traits@D@std@@V?$allocator@D@2@@std@@YAAAV?$basic_istream@DU?$char_traits@D@std@@@0@AAV10@AAV?$basic_string@DU?$char_traits@D@std@@V?$allocator@D@2@@0@@Z??6?$basic_ostream@DU?$char_traits@D@std@@@std@@QAEAAV01@P6AAAV01@AAV01@@Z@Z_??1?$basic_string@DU?$char_traits@D@std@@V?$allocator@D@2@@std@@QAE@XZ{??0?$basic_string@DU?$char_traits@D@std@@V?$allocator@D@2@@std@@QAE@XZ�?endl@std@@YAAAV?$basic_ostream@DU?$char_traits@D@std@@@1@AAV21@@ZMSVCP90.dll_amsg_exit�__getmainargs,_cexit|_exitf_XcptFilter�exit�__initenv_initterm_initterm_e<_configthreadlocale�__setusermatherr _adjust_fdiv�__p__commode�__p__fmodej_encode_pointer�__set_app_typeK_crt_debugger_hookC?terminate@@YAXXZMSVCR90.dll�_unlock�__dllonexitv_lock_onexit`_decode_pointers_except_handler4_common _invoke_watson?_controlfp_s�InterlockedExchange!Sleep�InterlockedCompareExchange-TerminateProcess�GetCurrentProcess>UnhandledExceptionFilterSetUnhandledExceptionFilter�IsDebuggerPresentTQueryPerformanceCounterfGetTickCount�GetCurrentThreadId�GetCurrentProcessIdOGetSystemTimeAsFileTimes__CxxFrameHandler3N�@���D������������$!@ �8�P�h� � ��@(��CV�(4VS_VERSION_INFO���StringFileInfob040904b0�QFileDescriptionReads from stdin without leaking info to the terminal and outputs back to stdout6 FileVersion1, 0, 0, 08 InternalNamehiddeninputPLegalCopyrightJordi Boggiano - 2012HOriginalFilenamehiddeninput.exe: ProductNameHidden Input: ProductVersion1, 0, 0, 0DVarFileInfo$Translation �<assembly xmlns="urn:schemas-microsoft-com:asm.v1" manifestVersion="1.0">
78353 <trustInfo xmlns="urn:schemas-microsoft-com:asm.v3">
78354 <security>
78355 <requestedPrivileges>
78356 <requestedExecutionLevel level="asInvoker" uiAccess="false"></requestedExecutionLevel>
78357 </requestedPrivileges>
78358 </security>
78359 </trustInfo>
78360 <dependency>
78361 <dependentAssembly>
78362 <assemblyIdentity type="win32" name="Microsoft.VC90.CRT" version="9.0.21022.8" processorArchitecture="x86" publicKeyToken="1fc8b3b9a1e18e3b"></assemblyIdentity>
78363 </dependentAssembly>
78364 </dependency>
78365 </assembly>PAPADDINGXXPADDINGPADDINGXXPADDINGPADDINGXXPADDINGPADDINGXXPADDINGPADDINGXXPADDINGPADDINGXXPADDINGPADDINGXXPADDINGPADDINGXXPADDINGPADDINGXXPADDINGPADDINGXXPADDINGPADDINGXXPADDINGPADDINGXXPADDINGPADDINGXXPADDINGPADDINGXXPADDINGPADDINGXXPADDINGPADDINGXXPADDINGPADDINGXXPADDINGPADDINGXXPADDINGPADDINGXXPADDINGPADDINGXXPADDINGPADDINGXXPADDINGPADDINGXXPADDINGPADDINGXXPADDINGPADDINGXXPADDINGPADDINGXXPADDINGPADDINGXXPADDINGPADDINGXXPADDINGPADDINGXXPADDINGPADDINGXXPADDINGPADDINGXXPADDING@00!0/080F0L0T0^0d0n0{0�0�0�0�0�0�0�0�0�0�0�0�0�01#1-1@1J1O1T1v1{1�1�1�1�1�1�1�1�1�1�1�1�1�1�12"2*23292A2M2_2j2p2�2�2�2�2�2�2�2�2�2�2�2 333%303N3T3Z3`3f3l3s3z3�3�3�3�3�3�3�3�3�3�3�3�3�3�3�3�34444%4;4B4�4�4�4�4�4�4�4�4�4�45!5^5c5�5�5�5H6M6_6}6�6�677 7*7w7|7�7�7�7�78 88=8E8P8V8\8b8h8n8t8z8�8�8�89 $�0�0�01 1t1x12 2@2\2`2h2t20 0CHANGELOG
78366 =========
78367
78368 2.3.0
78369 -----
78370
78371 * added multiselect support to the select dialog helper
78372 * added Table Helper for tabular data rendering
78373 * added support for events in `Application`
78374 * added a way to normalize EOLs in `ApplicationTester::getDisplay()` and `CommandTester::getDisplay()`
78375 * added a way to set the progress bar progress via the `setCurrent` method
78376 * added support for multiple InputOption shortcuts, written as `'-a|-b|-c'`
78377 * added two additional verbosity levels, VERBOSITY_VERY_VERBOSE and VERBOSITY_DEBUG
78378
78379 2.2.0
78380 -----
78381
78382 * added support for colorization on Windows via ConEmu
78383 * add a method to Dialog Helper to ask for a question and hide the response
78384 * added support for interactive selections in console (DialogHelper::select())
78385 * added support for autocompletion as you type in Dialog Helper
78386
78387 2.1.0
78388 -----
78389
78390 * added ConsoleOutputInterface
78391 * added the possibility to disable a command (Command::isEnabled())
78392 * added suggestions when a command does not exist
78393 * added a --raw option to the list command
78394 * added support for STDERR in the console output class (errors are now sent
78395 to STDERR)
78396 * made the defaults (helper set, commands, input definition) in Application
78397 more easily customizable
78398 * added support for the shell even if readline is not available
78399 * added support for process isolation in Symfony shell via
78400 `--process-isolation` switch
78401 * added support for `--`, which disables options parsing after that point
78402 (tokens will be parsed as arguments)
78403 <?php
78404
78405 /*
78406 * This file is part of the Symfony package.
78407 *
78408 * (c) Fabien Potencier <fabien@symfony.com>
78409 *
78410 * For the full copyright and license information, please view the LICENSE
78411 * file that was distributed with this source code.
78412 */
78413
78414 namespace Symfony\Component\Console;
78415
78416 use Symfony\Component\Console\Application;
78417 use Symfony\Component\Console\Input\StringInput;
78418 use Symfony\Component\Console\Output\ConsoleOutput;
78419 use Symfony\Component\Process\ProcessBuilder;
78420 use Symfony\Component\Process\PhpExecutableFinder;
78421
78422 /**
78423 * A Shell wraps an Application to add shell capabilities to it.
78424 *
78425 * Support for history and completion only works with a PHP compiled
78426 * with readline support (either --with-readline or --with-libedit)
78427 *
78428 * @author Fabien Potencier <fabien@symfony.com>
78429 * @author Martin Hasoň <martin.hason@gmail.com>
78430 */
78431 class Shell
78432 {
78433 private $application;
78434 private $history;
78435 private $output;
78436 private $hasReadline;
78437 private $processIsolation;
78438
78439 /**
78440 * Constructor.
78441 *
78442 * If there is no readline support for the current PHP executable
78443 * a \RuntimeException exception is thrown.
78444 *
78445 * @param Application $application An application instance
78446 */
78447 public function __construct(Application $application)
78448 {
78449 $this->hasReadline = function_exists('readline');
78450 $this->application = $application;
78451 $this->history = getenv('HOME').'/.history_'.$application->getName();
78452 $this->output = new ConsoleOutput();
78453 $this->processIsolation = false;
78454 }
78455
78456 /**
78457 * Runs the shell.
78458 */
78459 public function run()
78460 {
78461 $this->application->setAutoExit(false);
78462 $this->application->setCatchExceptions(true);
78463
78464 if ($this->hasReadline) {
78465 readline_read_history($this->history);
78466 readline_completion_function(array($this, 'autocompleter'));
78467 }
78468
78469 $this->output->writeln($this->getHeader());
78470 $php = null;
78471 if ($this->processIsolation) {
78472 $finder = new PhpExecutableFinder();
78473 $php = $finder->find();
78474 $this->output->writeln(<<<EOF
78475 <info>Running with process isolation, you should consider this:</info>
78476 * each command is executed as separate process,
78477 * commands don't support interactivity, all params must be passed explicitly,
78478 * commands output is not colorized.
78479
78480 EOF
78481 );
78482 }
78483
78484 while (true) {
78485 $command = $this->readline();
78486
78487 if (false === $command) {
78488 $this->output->writeln("\n");
78489
78490 break;
78491 }
78492
78493 if ($this->hasReadline) {
78494 readline_add_history($command);
78495 readline_write_history($this->history);
78496 }
78497
78498 if ($this->processIsolation) {
78499 $pb = new ProcessBuilder();
78500
78501 $process = $pb
78502 ->add($php)
78503 ->add($_SERVER['argv'][0])
78504 ->add($command)
78505 ->inheritEnvironmentVariables(true)
78506 ->getProcess()
78507 ;
78508
78509 $output = $this->output;
78510 $process->run(function($type, $data) use ($output) {
78511 $output->writeln($data);
78512 });
78513
78514 $ret = $process->getExitCode();
78515 } else {
78516 $ret = $this->application->run(new StringInput($command), $this->output);
78517 }
78518
78519 if (0 !== $ret) {
78520 $this->output->writeln(sprintf('<error>The command terminated with an error status (%s)</error>', $ret));
78521 }
78522 }
78523 }
78524
78525 /**
78526 * Returns the shell header.
78527 *
78528 * @return string The header string
78529 */
78530 protected function getHeader()
78531 {
78532 return <<<EOF
78533
78534 Welcome to the <info>{$this->application->getName()}</info> shell (<comment>{$this->application->getVersion()}</comment>).
78535
78536 At the prompt, type <comment>help</comment> for some help,
78537 or <comment>list</comment> to get a list of available commands.
78538
78539 To exit the shell, type <comment>^D</comment>.
78540
78541 EOF;
78542 }
78543
78544 /**
78545 * Renders a prompt.
78546 *
78547 * @return string The prompt
78548 */
78549 protected function getPrompt()
78550 {
78551 // using the formatter here is required when using readline
78552 return $this->output->getFormatter()->format($this->application->getName().' > ');
78553 }
78554
78555 protected function getOutput()
78556 {
78557 return $this->output;
78558 }
78559
78560 protected function getApplication()
78561 {
78562 return $this->application;
78563 }
78564
78565 /**
78566 * Tries to return autocompletion for the current entered text.
78567 *
78568 * @param string $text The last segment of the entered text
78569 *
78570 * @return Boolean|array A list of guessed strings or true
78571 */
78572 private function autocompleter($text)
78573 {
78574 $info = readline_info();
78575 $text = substr($info['line_buffer'], 0, $info['end']);
78576
78577 if ($info['point'] !== $info['end']) {
78578 return true;
78579 }
78580
78581 // task name?
78582 if (false === strpos($text, ' ') || !$text) {
78583 return array_keys($this->application->all());
78584 }
78585
78586 // options and arguments?
78587 try {
78588 $command = $this->application->find(substr($text, 0, strpos($text, ' ')));
78589 } catch (\Exception $e) {
78590 return true;
78591 }
78592
78593 $list = array('--help');
78594 foreach ($command->getDefinition()->getOptions() as $option) {
78595 $list[] = '--'.$option->getName();
78596 }
78597
78598 return $list;
78599 }
78600
78601 /**
78602 * Reads a single line from standard input.
78603 *
78604 * @return string The single line from standard input
78605 */
78606 private function readline()
78607 {
78608 if ($this->hasReadline) {
78609 $line = readline($this->getPrompt());
78610 } else {
78611 $this->output->write($this->getPrompt());
78612 $line = fgets(STDIN, 1024);
78613 $line = (!$line && strlen($line) == 0) ? false : rtrim($line);
78614 }
78615
78616 return $line;
78617 }
78618
78619 public function getProcessIsolation()
78620 {
78621 return $this->processIsolation;
78622 }
78623
78624 public function setProcessIsolation($processIsolation)
78625 {
78626 $this->processIsolation = (Boolean) $processIsolation;
78627
78628 if ($this->processIsolation && !class_exists('Symfony\\Component\\Process\\Process')) {
78629 throw new \RuntimeException('Unable to isolate processes as the Symfony Process Component is not installed.');
78630 }
78631 }
78632 }
78633 <?php
78634
78635 /*
78636 * This file is part of the Symfony package.
78637 *
78638 * (c) Fabien Potencier <fabien@symfony.com>
78639 *
78640 * For the full copyright and license information, please view the LICENSE
78641 * file that was distributed with this source code.
78642 */
78643
78644 namespace Symfony\Component\Console\Descriptor;
78645
78646 use Symfony\Component\Console\Application;
78647 use Symfony\Component\Console\Command\Command;
78648 use Symfony\Component\Console\Input\InputArgument;
78649 use Symfony\Component\Console\Input\InputDefinition;
78650 use Symfony\Component\Console\Input\InputOption;
78651
78652 /**
78653 * Markdown descriptor.
78654 *
78655 * @author Jean-François Simon <contact@jfsimon.fr>
78656 */
78657 class MarkdownDescriptor extends Descriptor
78658 {
78659 /**
78660 * {@inheritdoc}
78661 */
78662 protected function describeInputArgument(InputArgument $argument, array $options = array())
78663 {
78664 return '**'.$argument->getName().':**'."\n\n"
78665 .'* Name: '.($argument->getName() ?: '<none>')."\n"
78666 .'* Is required: '.($argument->isRequired() ? 'yes' : 'no')."\n"
78667 .'* Is array: '.($argument->isArray() ? 'yes' : 'no')."\n"
78668 .'* Description: '.($argument->getDescription() ?: '<none>')."\n"
78669 .'* Default: `'.str_replace("\n", '', var_export($argument->getDefault(), true)).'`';
78670 }
78671
78672 /**
78673 * {@inheritdoc}
78674 */
78675 protected function describeInputOption(InputOption $option, array $options = array())
78676 {
78677 return '**'.$option->getName().':**'."\n\n"
78678 .'* Name: `--'.$option->getName().'`'."\n"
78679 .'* Shortcut: '.($option->getShortcut() ? '`-'.implode('|-', explode('|', $option->getShortcut())).'`' : '<none>')."\n"
78680 .'* Accept value: '.($option->acceptValue() ? 'yes' : 'no')."\n"
78681 .'* Is value required: '.($option->isValueRequired() ? 'yes' : 'no')."\n"
78682 .'* Is multiple: '.($option->isArray() ? 'yes' : 'no')."\n"
78683 .'* Description: '.($option->getDescription() ?: '<none>')."\n"
78684 .'* Default: `'.str_replace("\n", '', var_export($option->getDefault(), true)).'`';
78685 }
78686
78687 /**
78688 * {@inheritdoc}
78689 */
78690 protected function describeInputDefinition(InputDefinition $definition, array $options = array())
78691 {
78692 $blocks = array();
78693
78694 if (count($definition->getArguments()) > 0) {
78695 $blocks[] = '### Arguments:';
78696 foreach ($definition->getArguments() as $argument) {
78697 $blocks[] = $this->describeInputArgument($argument);
78698 }
78699 }
78700
78701 if (count($definition->getOptions()) > 0) {
78702 $blocks[] = '### Options:';
78703 foreach ($definition->getOptions() as $option) {
78704 $blocks[] = $this->describeInputOption($option);
78705 }
78706 }
78707
78708 return implode("\n\n", $blocks);
78709 }
78710
78711 /**
78712 * {@inheritdoc}
78713 */
78714 protected function describeCommand(Command $command, array $options = array())
78715 {
78716 $command->getSynopsis();
78717 $command->mergeApplicationDefinition(false);
78718
78719 $markdown = $command->getName()."\n"
78720 .str_repeat('-', strlen($command->getName()))."\n\n"
78721 .'* Description: '.($command->getDescription() ?: '<none>')."\n"
78722 .'* Usage: `'.$command->getSynopsis().'`'."\n"
78723 .'* Aliases: '.(count($command->getAliases()) ? '`'.implode('`, `', $command->getAliases()).'`' : '<none>');
78724
78725 if ($help = $command->getProcessedHelp()) {
78726 $markdown .= "\n\n".$help;
78727 }
78728
78729 if ($definitionMarkdown = $this->describeInputDefinition($command->getNativeDefinition())) {
78730 $markdown .= "\n\n".$definitionMarkdown;
78731 }
78732
78733 return $markdown;
78734 }
78735
78736 /**
78737 * {@inheritdoc}
78738 */
78739 protected function describeApplication(Application $application, array $options = array())
78740 {
78741 $describedNamespace = isset($options['namespace']) ? $options['namespace'] : null;
78742 $description = new ApplicationDescription($application, $describedNamespace);
78743 $blocks = array($application->getName()."\n".str_repeat('=', strlen($application->getName())));
78744
78745 foreach ($description->getNamespaces() as $namespace) {
78746 if (ApplicationDescription::GLOBAL_NAMESPACE !== $namespace['id']) {
78747 $blocks[] = '**'.$namespace['id'].':**';
78748 }
78749
78750 $blocks[] = implode("\n", array_map(function ($commandName) {
78751 return '* '.$commandName;
78752 } , $namespace['commands']));
78753 }
78754
78755 foreach ($description->getCommands() as $command) {
78756 $blocks[] = $this->describeCommand($command);
78757 }
78758
78759 return implode("\n\n", $blocks);
78760 }
78761 }
78762 <?php
78763
78764 /*
78765 * This file is part of the Symfony package.
78766 *
78767 * (c) Fabien Potencier <fabien@symfony.com>
78768 *
78769 * For the full copyright and license information, please view the LICENSE
78770 * file that was distributed with this source code.
78771 */
78772
78773 namespace Symfony\Component\Console\Descriptor;
78774
78775 use Symfony\Component\Console\Application;
78776 use Symfony\Component\Console\Command\Command;
78777 use Symfony\Component\Console\Input\InputArgument;
78778 use Symfony\Component\Console\Input\InputDefinition;
78779 use Symfony\Component\Console\Input\InputOption;
78780
78781 /**
78782 * @author Jean-François Simon <jeanfrancois.simon@sensiolabs.com>
78783 */
78784 abstract class Descriptor implements DescriptorInterface
78785 {
78786 public function describe($object, array $options = array())
78787 {
78788 switch (true) {
78789 case $object instanceof InputArgument:
78790 return $this->describeInputArgument($object, $options);
78791 case $object instanceof InputOption:
78792 return $this->describeInputOption($object, $options);
78793 case $object instanceof InputDefinition:
78794 return $this->describeInputDefinition($object, $options);
78795 case $object instanceof Command:
78796 return $this->describeCommand($object, $options);
78797 case $object instanceof Application:
78798 return $this->describeApplication($object, $options);
78799 }
78800
78801 throw new \InvalidArgumentException(sprintf('Object of type "%s" is not describable.', get_class($object)));
78802 }
78803
78804 /**
78805 * Describes an InputArgument instance.
78806 *
78807 * @param InputArgument $argument
78808 * @param array $options
78809 *
78810 * @return string|mixed
78811 */
78812 abstract protected function describeInputArgument(InputArgument $argument, array $options = array());
78813
78814 /**
78815 * Describes an InputOption instance.
78816 *
78817 * @param InputOption $option
78818 * @param array $options
78819 *
78820 * @return string|mixed
78821 */
78822 abstract protected function describeInputOption(InputOption $option, array $options = array());
78823
78824 /**
78825 * Describes an InputDefinition instance.
78826 *
78827 * @param InputDefinition $definition
78828 * @param array $options
78829 *
78830 * @return string|mixed
78831 */
78832 abstract protected function describeInputDefinition(InputDefinition $definition, array $options = array());
78833
78834 /**
78835 * Describes a Command instance.
78836 *
78837 * @param Command $command
78838 * @param array $options
78839 *
78840 * @return string|mixed
78841 */
78842 abstract protected function describeCommand(Command $command, array $options = array());
78843
78844 /**
78845 * Describes an Application instance.
78846 *
78847 * @param Application $application
78848 * @param array $options
78849 *
78850 * @return string|mixed
78851 */
78852 abstract protected function describeApplication(Application $application, array $options = array());
78853 }
78854 <?php
78855
78856 /*
78857 * This file is part of the Symfony package.
78858 *
78859 * (c) Fabien Potencier <fabien@symfony.com>
78860 *
78861 * For the full copyright and license information, please view the LICENSE
78862 * file that was distributed with this source code.
78863 */
78864
78865 namespace Symfony\Component\Console\Descriptor;
78866
78867 /**
78868 * Descriptor interface.
78869 *
78870 * @author Jean-François Simon <contact@jfsimon.fr>
78871 */
78872 interface DescriptorInterface
78873 {
78874 /**
78875 * Describes an InputArgument instance.
78876 *
78877 * @param object $object
78878 * @param array $options
78879 *
78880 * @return string|mixed
78881 */
78882 public function describe($object, array $options = array());
78883 }
78884 <?php
78885
78886 /*
78887 * This file is part of the Symfony package.
78888 *
78889 * (c) Fabien Potencier <fabien@symfony.com>
78890 *
78891 * For the full copyright and license information, please view the LICENSE
78892 * file that was distributed with this source code.
78893 */
78894
78895 namespace Symfony\Component\Console\Descriptor;
78896
78897 use Symfony\Component\Console\Application;
78898 use Symfony\Component\Console\Command\Command;
78899 use Symfony\Component\Console\Input\InputArgument;
78900 use Symfony\Component\Console\Input\InputDefinition;
78901 use Symfony\Component\Console\Input\InputOption;
78902
78903 /**
78904 * Text descriptor.
78905 *
78906 * @author Jean-François Simon <contact@jfsimon.fr>
78907 */
78908 class TextDescriptor extends Descriptor
78909 {
78910 /**
78911 * {@inheritdoc}
78912 */
78913 protected function describeInputArgument(InputArgument $argument, array $options = array())
78914 {
78915 if (null !== $argument->getDefault() && (!is_array($argument->getDefault()) || count($argument->getDefault()))) {
78916 $default = sprintf('<comment> (default: %s)</comment>', $this->formatDefaultValue($argument->getDefault()));
78917 } else {
78918 $default = '';
78919 }
78920
78921 $nameWidth = isset($options['name_width']) ? $options['name_width'] : strlen($argument->getName());
78922 $output = str_replace("\n", "\n".str_repeat(' ', $nameWidth + 2), $argument->getDescription());
78923 $output = sprintf(" <info>%-${nameWidth}s</info> %s%s", $argument->getName(), $output, $default);
78924
78925 return isset($options['raw_text']) && $options['raw_text'] ? strip_tags($output) : $output;
78926 }
78927
78928 /**
78929 * {@inheritdoc}
78930 */
78931 protected function describeInputOption(InputOption $option, array $options = array())
78932 {
78933 if ($option->acceptValue() && null !== $option->getDefault() && (!is_array($option->getDefault()) || count($option->getDefault()))) {
78934 $default = sprintf('<comment> (default: %s)</comment>', $this->formatDefaultValue($option->getDefault()));
78935 } else {
78936 $default = '';
78937 }
78938
78939 $nameWidth = isset($options['name_width']) ? $options['name_width'] : strlen($option->getName());
78940 $nameWithShortcutWidth = $nameWidth - strlen($option->getName()) - 2;
78941
78942 $output = sprintf(" <info>%s</info> %-${nameWithShortcutWidth}s%s%s%s",
78943 '--'.$option->getName(),
78944 $option->getShortcut() ? sprintf('(-%s) ', $option->getShortcut()) : '',
78945 str_replace("\n", "\n".str_repeat(' ', $nameWidth + 2), $option->getDescription()),
78946 $default,
78947 $option->isArray() ? '<comment> (multiple values allowed)</comment>' : ''
78948 );
78949
78950 return isset($options['raw_text']) && $options['raw_text'] ? strip_tags($output) : $output;
78951 }
78952
78953 /**
78954 * {@inheritdoc}
78955 */
78956 protected function describeInputDefinition(InputDefinition $definition, array $options = array())
78957 {
78958 $nameWidth = 0;
78959 foreach ($definition->getOptions() as $option) {
78960 $nameLength = strlen($option->getName()) + 2;
78961 if ($option->getShortcut()) {
78962 $nameLength += strlen($option->getShortcut()) + 3;
78963 }
78964 $nameWidth = max($nameWidth, $nameLength);
78965 }
78966 foreach ($definition->getArguments() as $argument) {
78967 $nameWidth = max($nameWidth, strlen($argument->getName()));
78968 }
78969 ++$nameWidth;
78970
78971 $messages = array();
78972
78973 if ($definition->getArguments()) {
78974 $messages[] = '<comment>Arguments:</comment>';
78975 foreach ($definition->getArguments() as $argument) {
78976 $messages[] = $this->describeInputArgument($argument, array('name_width' => $nameWidth));
78977 }
78978 $messages[] = '';
78979 }
78980
78981 if ($definition->getOptions()) {
78982 $messages[] = '<comment>Options:</comment>';
78983 foreach ($definition->getOptions() as $option) {
78984 $messages[] = $this->describeInputOption($option, array('name_width' => $nameWidth));
78985 }
78986 $messages[] = '';
78987 }
78988
78989 $output = implode("\n", $messages);
78990
78991 return isset($options['raw_text']) && $options['raw_text'] ? strip_tags($output) : $output;
78992 }
78993
78994 /**
78995 * {@inheritdoc}
78996 */
78997 protected function describeCommand(Command $command, array $options = array())
78998 {
78999 $command->getSynopsis();
79000 $command->mergeApplicationDefinition(false);
79001 $messages = array('<comment>Usage:</comment>', ' '.$command->getSynopsis(), '');
79002
79003 if ($command->getAliases()) {
79004 $messages[] = '<comment>Aliases:</comment> <info>'.implode(', ', $command->getAliases()).'</info>';
79005 }
79006
79007 $messages[] = $this->describeInputDefinition($command->getNativeDefinition());
79008
79009 if ($help = $command->getProcessedHelp()) {
79010 $messages[] = '<comment>Help:</comment>';
79011 $messages[] = ' '.str_replace("\n", "\n ", $help)."\n";
79012 }
79013
79014 $output = implode("\n", $messages);
79015
79016 return isset($options['raw_text']) && $options['raw_text'] ? strip_tags($output) : $output;
79017 }
79018
79019 /**
79020 * {@inheritdoc}
79021 */
79022 protected function describeApplication(Application $application, array $options = array())
79023 {
79024 $describedNamespace = isset($options['namespace']) ? $options['namespace'] : null;
79025 $description = new ApplicationDescription($application, $describedNamespace);
79026 $messages = array();
79027
79028 if (isset($options['raw_text']) && $options['raw_text']) {
79029 $width = $this->getColumnWidth($description->getCommands());
79030
79031 foreach ($description->getCommands() as $command) {
79032 $messages[] = sprintf("%-${width}s %s", $command->getName(), $command->getDescription());
79033 }
79034 } else {
79035 $width = $this->getColumnWidth($description->getCommands());
79036
79037 $messages[] = $application->getHelp();
79038 $messages[] = '';
79039
79040 if ($describedNamespace) {
79041 $messages[] = sprintf("<comment>Available commands for the \"%s\" namespace:</comment>", $describedNamespace);
79042 } else {
79043 $messages[] = '<comment>Available commands:</comment>';
79044 }
79045
79046 // add commands by namespace
79047 foreach ($description->getNamespaces() as $namespace) {
79048 if (!$describedNamespace && ApplicationDescription::GLOBAL_NAMESPACE !== $namespace['id']) {
79049 $messages[] = '<comment>'.$namespace['id'].'</comment>';
79050 }
79051
79052 foreach ($namespace['commands'] as $name) {
79053 $messages[] = sprintf(" <info>%-${width}s</info> %s", $name, $description->getCommand($name)->getDescription());
79054 }
79055 }
79056 }
79057
79058 $output = implode("\n", $messages);
79059
79060 return isset($options['raw_text']) && $options['raw_text'] ? strip_tags($output) : $output;
79061 }
79062
79063 /**
79064 * Formats input option/argument default value.
79065 *
79066 * @param mixed $default
79067 *
79068 * @return string
79069 */
79070 private function formatDefaultValue($default)
79071 {
79072 if (version_compare(PHP_VERSION, '5.4', '<')) {
79073 return str_replace('\/', '/', json_encode($default));
79074 }
79075
79076 return json_encode($default, JSON_UNESCAPED_SLASHES | JSON_UNESCAPED_UNICODE);
79077 }
79078
79079 /**
79080 * @param Command[] $commands
79081 *
79082 * @return int
79083 */
79084 private function getColumnWidth(array $commands)
79085 {
79086 $width = 0;
79087 foreach ($commands as $command) {
79088 $width = strlen($command->getName()) > $width ? strlen($command->getName()) : $width;
79089 }
79090
79091 return $width + 2;
79092 }
79093 }
79094 <?php
79095
79096 /*
79097 * This file is part of the Symfony package.
79098 *
79099 * (c) Fabien Potencier <fabien@symfony.com>
79100 *
79101 * For the full copyright and license information, please view the LICENSE
79102 * file that was distributed with this source code.
79103 */
79104
79105 namespace Symfony\Component\Console\Descriptor;
79106
79107 use Symfony\Component\Console\Application;
79108 use Symfony\Component\Console\Command\Command;
79109
79110 /**
79111 * @author Jean-François Simon <jeanfrancois.simon@sensiolabs.com>
79112 */
79113 class ApplicationDescription
79114 {
79115 const GLOBAL_NAMESPACE = '_global';
79116
79117 /**
79118 * @var Application
79119 */
79120 private $application;
79121
79122 /**
79123 * @var null|string
79124 */
79125 private $namespace;
79126
79127 /**
79128 * @var array
79129 */
79130 private $namespaces;
79131
79132 /**
79133 * @var Command[]
79134 */
79135 private $commands;
79136
79137 /**
79138 * @var Command[]
79139 */
79140 private $aliases;
79141
79142 /**
79143 * Constructor.
79144 *
79145 * @param Application $application
79146 * @param string|null $namespace
79147 */
79148 public function __construct(Application $application, $namespace = null)
79149 {
79150 $this->application = $application;
79151 $this->namespace = $namespace;
79152 }
79153
79154 /**
79155 * @return array
79156 */
79157 public function getNamespaces()
79158 {
79159 if (null === $this->namespaces) {
79160 $this->inspectApplication();
79161 }
79162
79163 return $this->namespaces;
79164 }
79165
79166 /**
79167 * @return Command[]
79168 */
79169 public function getCommands()
79170 {
79171 if (null === $this->commands) {
79172 $this->inspectApplication();
79173 }
79174
79175 return $this->commands;
79176 }
79177
79178 /**
79179 * @param string $name
79180 *
79181 * @return Command
79182 *
79183 * @throws \InvalidArgumentException
79184 */
79185 public function getCommand($name)
79186 {
79187 if (!isset($this->commands[$name]) && !isset($this->aliases[$name])) {
79188 throw new \InvalidArgumentException(sprintf('Command %s does not exist.', $name));
79189 }
79190
79191 return isset($this->commands[$name]) ? $this->commands[$name] : $this->aliases[$name];
79192 }
79193
79194 private function inspectApplication()
79195 {
79196 $this->commands = array();
79197 $this->namespaces = array();
79198
79199 $all = $this->application->all($this->namespace ? $this->application->findNamespace($this->namespace) : null);
79200 foreach ($this->sortCommands($all) as $namespace => $commands) {
79201 $names = array();
79202
79203 /** @var Command $command */
79204 foreach ($commands as $name => $command) {
79205 if (!$command->getName()) {
79206 continue;
79207 }
79208
79209 if ($command->getName() === $name) {
79210 $this->commands[$name] = $command;
79211 } else {
79212 $this->aliases[$name] = $command;
79213 }
79214
79215 $names[] = $name;
79216 }
79217
79218 $this->namespaces[$namespace] = array('id' => $namespace, 'commands' => $names);
79219 }
79220 }
79221
79222 /**
79223 * @param array $commands
79224 *
79225 * @return array
79226 */
79227 private function sortCommands(array $commands)
79228 {
79229 $namespacedCommands = array();
79230 foreach ($commands as $name => $command) {
79231 $key = $this->application->extractNamespace($name, 1);
79232 if (!$key) {
79233 $key = '_global';
79234 }
79235
79236 $namespacedCommands[$key][$name] = $command;
79237 }
79238 ksort($namespacedCommands);
79239
79240 foreach ($namespacedCommands as &$commands) {
79241 ksort($commands);
79242 }
79243
79244 return $namespacedCommands;
79245 }
79246 }
79247 <?php
79248
79249 /*
79250 * This file is part of the Symfony package.
79251 *
79252 * (c) Fabien Potencier <fabien@symfony.com>
79253 *
79254 * For the full copyright and license information, please view the LICENSE
79255 * file that was distributed with this source code.
79256 */
79257
79258 namespace Symfony\Component\Console\Descriptor;
79259
79260 use Symfony\Component\Console\Application;
79261 use Symfony\Component\Console\Command\Command;
79262 use Symfony\Component\Console\Input\InputArgument;
79263 use Symfony\Component\Console\Input\InputDefinition;
79264 use Symfony\Component\Console\Input\InputOption;
79265
79266 /**
79267 * JSON descriptor.
79268 *
79269 * @author Jean-François Simon <contact@jfsimon.fr>
79270 */
79271 class JsonDescriptor extends Descriptor
79272 {
79273 /**
79274 * {@inheritdoc}
79275 */
79276 protected function describeInputArgument(InputArgument $argument, array $options = array())
79277 {
79278 return $this->output(array(
79279 'name' => $argument->getName(),
79280 'is_required' => $argument->isRequired(),
79281 'is_array' => $argument->isArray(),
79282 'description' => $argument->getDescription(),
79283 'default' => $argument->getDefault(),
79284 ), $options);
79285 }
79286
79287 /**
79288 * {@inheritdoc}
79289 */
79290 protected function describeInputOption(InputOption $option, array $options = array())
79291 {
79292 return $this->output(array(
79293 'name' => '--'.$option->getName(),
79294 'shortcut' => $option->getShortcut() ? '-'.implode('|-', explode('|', $option->getShortcut())) : '',
79295 'accept_value' => $option->acceptValue(),
79296 'is_value_required' => $option->isValueRequired(),
79297 'is_multiple' => $option->isArray(),
79298 'description' => $option->getDescription(),
79299 'default' => $option->getDefault(),
79300 ), $options);
79301 }
79302
79303 /**
79304 * {@inheritdoc}
79305 */
79306 protected function describeInputDefinition(InputDefinition $definition, array $options = array())
79307 {
79308 $inputArguments = array();
79309 foreach ($definition->getArguments() as $name => $argument) {
79310 $inputArguments[$name] = $this->describeInputArgument($argument, array('as_array' => true));
79311 }
79312
79313 $inputOptions = array();
79314 foreach ($definition->getOptions() as $name => $option) {
79315 $inputOptions[$name] = $this->describeInputOption($option, array('as_array' => true));
79316 }
79317
79318 return $this->output(array('arguments' => $inputArguments, 'options' => $inputOptions), $options);
79319 }
79320
79321 /**
79322 * {@inheritdoc}
79323 */
79324 protected function describeCommand(Command $command, array $options = array())
79325 {
79326 $command->getSynopsis();
79327 $command->mergeApplicationDefinition(false);
79328
79329 return $this->output(array(
79330 'name' => $command->getName(),
79331 'usage' => $command->getSynopsis(),
79332 'description' => $command->getDescription(),
79333 'help' => $command->getProcessedHelp(),
79334 'aliases' => $command->getAliases(),
79335 'definition' => $this->describeInputDefinition($command->getNativeDefinition(), array('as_array' => true)),
79336 ), $options);
79337 }
79338
79339 /**
79340 * {@inheritdoc}
79341 */
79342 protected function describeApplication(Application $application, array $options = array())
79343 {
79344 $describedNamespace = isset($options['namespace']) ? $options['namespace'] : null;
79345 $description = new ApplicationDescription($application, $describedNamespace);
79346 $commands = array();
79347
79348 foreach ($description->getCommands() as $command) {
79349 $commands[] = $this->describeCommand($command, array('as_array' => true));
79350 }
79351
79352 $data = $describedNamespace
79353 ? array('commands' => $commands, 'namespace' => $describedNamespace)
79354 : array('commands' => $commands, 'namespaces' => array_values($description->getNamespaces()));
79355
79356 return $this->output($data, $options);
79357 }
79358
79359 /**
79360 * Outputs data as array or string according to options.
79361 *
79362 * @param array $data
79363 * @param array $options
79364 *
79365 * @return array|string
79366 */
79367 private function output(array $data, array $options)
79368 {
79369 if (isset($options['as_array']) && $options['as_array']) {
79370 return $data;
79371 }
79372
79373 return json_encode($data, isset($options['json_encoding']) ? $options['json_encoding'] : 0);
79374 }
79375 }
79376 <?php
79377
79378 /*
79379 * This file is part of the Symfony package.
79380 *
79381 * (c) Fabien Potencier <fabien@symfony.com>
79382 *
79383 * For the full copyright and license information, please view the LICENSE
79384 * file that was distributed with this source code.
79385 */
79386
79387 namespace Symfony\Component\Console\Descriptor;
79388
79389 use Symfony\Component\Console\Application;
79390 use Symfony\Component\Console\Command\Command;
79391 use Symfony\Component\Console\Input\InputArgument;
79392 use Symfony\Component\Console\Input\InputDefinition;
79393 use Symfony\Component\Console\Input\InputOption;
79394
79395 /**
79396 * XML descriptor.
79397 *
79398 * @author Jean-François Simon <contact@jfsimon.fr>
79399 */
79400 class XmlDescriptor extends Descriptor
79401 {
79402 /**
79403 * {@inheritdoc}
79404 */
79405 protected function describeInputArgument(InputArgument $argument, array $options = array())
79406 {
79407 $dom = new \DOMDocument('1.0', 'UTF-8');
79408
79409 $dom->appendChild($objectXML = $dom->createElement('argument'));
79410 $objectXML->setAttribute('name', $argument->getName());
79411 $objectXML->setAttribute('is_required', $argument->isRequired() ? 1 : 0);
79412 $objectXML->setAttribute('is_array', $argument->isArray() ? 1 : 0);
79413 $objectXML->appendChild($descriptionXML = $dom->createElement('description'));
79414 $descriptionXML->appendChild($dom->createTextNode($argument->getDescription()));
79415
79416 $objectXML->appendChild($defaultsXML = $dom->createElement('defaults'));
79417 $defaults = is_array($argument->getDefault()) ? $argument->getDefault() : (is_bool($argument->getDefault()) ? array(var_export($argument->getDefault(), true)) : ($argument->getDefault() ? array($argument->getDefault()) : array()));
79418 foreach ($defaults as $default) {
79419 $defaultsXML->appendChild($defaultXML = $dom->createElement('default'));
79420 $defaultXML->appendChild($dom->createTextNode($default));
79421 }
79422
79423 return $this->output($dom, $options);
79424 }
79425
79426 /**
79427 * {@inheritdoc}
79428 */
79429 protected function describeInputOption(InputOption $option, array $options = array())
79430 {
79431 $dom = new \DOMDocument('1.0', 'UTF-8');
79432
79433 $dom->appendChild($objectXML = $dom->createElement('option'));
79434 $objectXML->setAttribute('name', '--'.$option->getName());
79435 $pos = strpos($option->getShortcut(), '|');
79436 if (false !== $pos) {
79437 $objectXML->setAttribute('shortcut', '-'.substr($option->getShortcut(), 0, $pos));
79438 $objectXML->setAttribute('shortcuts', '-'.implode('|-', explode('|', $option->getShortcut())));
79439 } else {
79440 $objectXML->setAttribute('shortcut', $option->getShortcut() ? '-'.$option->getShortcut() : '');
79441 }
79442 $objectXML->setAttribute('accept_value', $option->acceptValue() ? 1 : 0);
79443 $objectXML->setAttribute('is_value_required', $option->isValueRequired() ? 1 : 0);
79444 $objectXML->setAttribute('is_multiple', $option->isArray() ? 1 : 0);
79445 $objectXML->appendChild($descriptionXML = $dom->createElement('description'));
79446 $descriptionXML->appendChild($dom->createTextNode($option->getDescription()));
79447
79448 if ($option->acceptValue()) {
79449 $defaults = is_array($option->getDefault()) ? $option->getDefault() : (is_bool($option->getDefault()) ? array(var_export($option->getDefault(), true)) : ($option->getDefault() ? array($option->getDefault()) : array()));
79450 $objectXML->appendChild($defaultsXML = $dom->createElement('defaults'));
79451
79452 if (!empty($defaults)) {
79453 foreach ($defaults as $default) {
79454 $defaultsXML->appendChild($defaultXML = $dom->createElement('default'));
79455 $defaultXML->appendChild($dom->createTextNode($default));
79456 }
79457 }
79458 }
79459
79460 return $this->output($dom, $options);
79461 }
79462
79463 /**
79464 * {@inheritdoc}
79465 */
79466 protected function describeInputDefinition(InputDefinition $definition, array $options = array())
79467 {
79468 $dom = new \DOMDocument('1.0', 'UTF-8');
79469 $dom->appendChild($definitionXML = $dom->createElement('definition'));
79470
79471 $definitionXML->appendChild($argumentsXML = $dom->createElement('arguments'));
79472 foreach ($definition->getArguments() as $argument) {
79473 $this->appendDocument($argumentsXML, $this->describeInputArgument($argument, array('as_dom' => true)));
79474 }
79475
79476 $definitionXML->appendChild($optionsXML = $dom->createElement('options'));
79477 foreach ($definition->getOptions() as $option) {
79478 $this->appendDocument($optionsXML, $this->describeInputOption($option, array('as_dom' => true)));
79479 }
79480
79481 return $this->output($dom, $options);
79482 }
79483
79484 /**
79485 * {@inheritdoc}
79486 */
79487 protected function describeCommand(Command $command, array $options = array())
79488 {
79489 $dom = new \DOMDocument('1.0', 'UTF-8');
79490 $dom->appendChild($commandXML = $dom->createElement('command'));
79491
79492 $command->getSynopsis();
79493 $command->mergeApplicationDefinition(false);
79494
79495 $commandXML->setAttribute('id', $command->getName());
79496 $commandXML->setAttribute('name', $command->getName());
79497
79498 $commandXML->appendChild($usageXML = $dom->createElement('usage'));
79499 $usageXML->appendChild($dom->createTextNode(sprintf($command->getSynopsis(), '')));
79500
79501 $commandXML->appendChild($descriptionXML = $dom->createElement('description'));
79502 $descriptionXML->appendChild($dom->createTextNode(str_replace("\n", "\n ", $command->getDescription())));
79503
79504 $commandXML->appendChild($helpXML = $dom->createElement('help'));
79505 $helpXML->appendChild($dom->createTextNode(str_replace("\n", "\n ", $command->getProcessedHelp())));
79506
79507 $commandXML->appendChild($aliasesXML = $dom->createElement('aliases'));
79508 foreach ($command->getAliases() as $alias) {
79509 $aliasesXML->appendChild($aliasXML = $dom->createElement('alias'));
79510 $aliasXML->appendChild($dom->createTextNode($alias));
79511 }
79512
79513 $definitionXML = $this->describeInputDefinition($command->getNativeDefinition(), array('as_dom' => true));
79514 $this->appendDocument($commandXML, $definitionXML->getElementsByTagName('definition')->item(0));
79515
79516 return $this->output($dom, $options);
79517 }
79518
79519 /**
79520 * {@inheritdoc}
79521 */
79522 protected function describeApplication(Application $application, array $options = array())
79523 {
79524 $dom = new \DOMDocument('1.0', 'UTF-8');
79525 $dom->appendChild($rootXml = $dom->createElement('symfony'));
79526 $rootXml->appendChild($commandsXML = $dom->createElement('commands'));
79527
79528 $describedNamespace = isset($options['namespace']) ? $options['namespace'] : null;
79529 $description = new ApplicationDescription($application, $describedNamespace);
79530
79531 if ($describedNamespace) {
79532 $commandsXML->setAttribute('namespace', $describedNamespace);
79533 }
79534
79535 foreach ($description->getCommands() as $command) {
79536 $this->appendDocument($commandsXML, $this->describeCommand($command, array('as_dom' => true)));
79537 }
79538
79539 if (!$describedNamespace) {
79540 $rootXml->appendChild($namespacesXML = $dom->createElement('namespaces'));
79541
79542 foreach ($description->getNamespaces() as $namespace) {
79543 $namespacesXML->appendChild($namespaceArrayXML = $dom->createElement('namespace'));
79544 $namespaceArrayXML->setAttribute('id', $namespace['id']);
79545
79546 foreach ($namespace['commands'] as $name) {
79547 $namespaceArrayXML->appendChild($commandXML = $dom->createElement('command'));
79548 $commandXML->appendChild($dom->createTextNode($name));
79549 }
79550 }
79551 }
79552
79553 return $this->output($dom, $options);
79554 }
79555
79556 /**
79557 * Appends document children to parent node.
79558 *
79559 * @param \DOMNode $parentNode
79560 * @param \DOMNode $importedParent
79561 */
79562 private function appendDocument(\DOMNode $parentNode, \DOMNode $importedParent)
79563 {
79564 foreach ($importedParent->childNodes as $childNode) {
79565 $parentNode->appendChild($parentNode->ownerDocument->importNode($childNode, true));
79566 }
79567 }
79568
79569 /**
79570 * Outputs document as DOMDocument or string according to options.
79571 *
79572 * @param \DOMDocument $dom
79573 * @param array $options
79574 *
79575 * @return \DOMDocument|string
79576 */
79577 private function output(\DOMDocument $dom, array $options)
79578 {
79579 if (isset($options['as_dom']) && $options['as_dom']) {
79580 return $dom;
79581 }
79582
79583 $dom->formatOutput = true;
79584
79585 return $dom->saveXML();
79586 }
79587 }
79588 {
79589 "name": "symfony/console",
79590 "type": "library",
79591 "description": "Symfony Console Component",
79592 "keywords": [],
79593 "homepage": "http://symfony.com",
79594 "license": "MIT",
79595 "authors": [
79596 {
79597 "name": "Fabien Potencier",
79598 "email": "fabien@symfony.com"
79599 },
79600 {
79601 "name": "Symfony Community",
79602 "homepage": "http://symfony.com/contributors"
79603 }
79604 ],
79605 "require": {
79606 "php": ">=5.3.3"
79607 },
79608 "require-dev": {
79609 "symfony/event-dispatcher": "~2.1"
79610 },
79611 "suggest": {
79612 "symfony/event-dispatcher": ""
79613 },
79614 "autoload": {
79615 "psr-0": { "Symfony\\Component\\Console\\": "" }
79616 },
79617 "target-dir": "Symfony/Component/Console",
79618 "minimum-stability": "dev",
79619 "extra": {
79620 "branch-alias": {
79621 "dev-master": "2.3-dev"
79622 }
79623 }
79624 }
79625 <?xml version="1.0" encoding="UTF-8"?>
79626
79627 <phpunit backupGlobals="false"
79628 backupStaticAttributes="false"
79629 colors="true"
79630 convertErrorsToExceptions="true"
79631 convertNoticesToExceptions="true"
79632 convertWarningsToExceptions="true"
79633 processIsolation="false"
79634 stopOnFailure="false"
79635 syntaxCheck="false"
79636 bootstrap="vendor/autoload.php"
79637 >
79638 <testsuites>
79639 <testsuite name="Symfony Console Component Test Suite">
79640 <directory>./Tests/</directory>
79641 </testsuite>
79642 </testsuites>
79643
79644 <filter>
79645 <whitelist>
79646 <directory>./</directory>
79647 <exclude>
79648 <directory>./Resources</directory>
79649 <directory>./Tests</directory>
79650 <directory>./vendor</directory>
79651 </exclude>
79652 </whitelist>
79653 </filter>
79654 </phpunit>
79655 <?php
79656
79657 /*
79658 * This file is part of the Symfony package.
79659 *
79660 * (c) Fabien Potencier <fabien@symfony.com>
79661 *
79662 * For the full copyright and license information, please view the LICENSE
79663 * file that was distributed with this source code.
79664 */
79665
79666 namespace Symfony\Component\Console\Command;
79667
79668 use Symfony\Component\Console\Helper\DescriptorHelper;
79669 use Symfony\Component\Console\Input\InputArgument;
79670 use Symfony\Component\Console\Input\InputOption;
79671 use Symfony\Component\Console\Input\InputInterface;
79672 use Symfony\Component\Console\Output\OutputInterface;
79673 use Symfony\Component\Console\Input\InputDefinition;
79674
79675 /**
79676 * ListCommand displays the list of all available commands for the application.
79677 *
79678 * @author Fabien Potencier <fabien@symfony.com>
79679 */
79680 class ListCommand extends Command
79681 {
79682 /**
79683 * {@inheritdoc}
79684 */
79685 protected function configure()
79686 {
79687 $this
79688 ->setName('list')
79689 ->setDefinition($this->createDefinition())
79690 ->setDescription('Lists commands')
79691 ->setHelp(<<<EOF
79692 The <info>%command.name%</info> command lists all commands:
79693
79694 <info>php %command.full_name%</info>
79695
79696 You can also display the commands for a specific namespace:
79697
79698 <info>php %command.full_name% test</info>
79699
79700 You can also output the information in other formats by using the <comment>--format</comment> option:
79701
79702 <info>php %command.full_name% --format=xml</info>
79703
79704 It's also possible to get raw list of commands (useful for embedding command runner):
79705
79706 <info>php %command.full_name% --raw</info>
79707 EOF
79708 )
79709 ;
79710 }
79711
79712 /**
79713 * {@inheritdoc}
79714 */
79715 public function getNativeDefinition()
79716 {
79717 return $this->createDefinition();
79718 }
79719
79720 /**
79721 * {@inheritdoc}
79722 */
79723 protected function execute(InputInterface $input, OutputInterface $output)
79724 {
79725 if ($input->getOption('xml')) {
79726 $input->setOption('format', 'xml');
79727 }
79728
79729 $helper = new DescriptorHelper();
79730 $helper->describe($output, $this->getApplication(), $input->getOption('format'), $input->getOption('raw'), $input->getArgument('namespace'));
79731 }
79732
79733 /**
79734 * {@inheritdoc}
79735 */
79736 private function createDefinition()
79737 {
79738 return new InputDefinition(array(
79739 new InputArgument('namespace', InputArgument::OPTIONAL, 'The namespace name'),
79740 new InputOption('xml', null, InputOption::VALUE_NONE, 'To output list as XML'),
79741 new InputOption('raw', null, InputOption::VALUE_NONE, 'To output raw command list'),
79742 new InputOption('format', null, InputOption::VALUE_REQUIRED, 'To output list in other formats'),
79743 ));
79744 }
79745 }
79746 <?php
79747
79748 /*
79749 * This file is part of the Symfony package.
79750 *
79751 * (c) Fabien Potencier <fabien@symfony.com>
79752 *
79753 * For the full copyright and license information, please view the LICENSE
79754 * file that was distributed with this source code.
79755 */
79756
79757 namespace Symfony\Component\Console\Command;
79758
79759 use Symfony\Component\Console\Descriptor\TextDescriptor;
79760 use Symfony\Component\Console\Descriptor\XmlDescriptor;
79761 use Symfony\Component\Console\Input\InputDefinition;
79762 use Symfony\Component\Console\Input\InputOption;
79763 use Symfony\Component\Console\Input\InputArgument;
79764 use Symfony\Component\Console\Input\InputInterface;
79765 use Symfony\Component\Console\Output\OutputInterface;
79766 use Symfony\Component\Console\Application;
79767 use Symfony\Component\Console\Helper\HelperSet;
79768
79769 /**
79770 * Base class for all commands.
79771 *
79772 * @author Fabien Potencier <fabien@symfony.com>
79773 *
79774 * @api
79775 */
79776 class Command
79777 {
79778 private $application;
79779 private $name;
79780 private $aliases;
79781 private $definition;
79782 private $help;
79783 private $description;
79784 private $ignoreValidationErrors;
79785 private $applicationDefinitionMerged;
79786 private $applicationDefinitionMergedWithArgs;
79787 private $code;
79788 private $synopsis;
79789 private $helperSet;
79790
79791 /**
79792 * Constructor.
79793 *
79794 * @param string $name The name of the command
79795 *
79796 * @throws \LogicException When the command name is empty
79797 *
79798 * @api
79799 */
79800 public function __construct($name = null)
79801 {
79802 $this->definition = new InputDefinition();
79803 $this->ignoreValidationErrors = false;
79804 $this->applicationDefinitionMerged = false;
79805 $this->applicationDefinitionMergedWithArgs = false;
79806 $this->aliases = array();
79807
79808 if (null !== $name) {
79809 $this->setName($name);
79810 }
79811
79812 $this->configure();
79813
79814 if (!$this->name) {
79815 throw new \LogicException('The command name cannot be empty.');
79816 }
79817 }
79818
79819 /**
79820 * Ignores validation errors.
79821 *
79822 * This is mainly useful for the help command.
79823 */
79824 public function ignoreValidationErrors()
79825 {
79826 $this->ignoreValidationErrors = true;
79827 }
79828
79829 /**
79830 * Sets the application instance for this command.
79831 *
79832 * @param Application $application An Application instance
79833 *
79834 * @api
79835 */
79836 public function setApplication(Application $application = null)
79837 {
79838 $this->application = $application;
79839 if ($application) {
79840 $this->setHelperSet($application->getHelperSet());
79841 } else {
79842 $this->helperSet = null;
79843 }
79844 }
79845
79846 /**
79847 * Sets the helper set.
79848 *
79849 * @param HelperSet $helperSet A HelperSet instance
79850 */
79851 public function setHelperSet(HelperSet $helperSet)
79852 {
79853 $this->helperSet = $helperSet;
79854 }
79855
79856 /**
79857 * Gets the helper set.
79858 *
79859 * @return HelperSet A HelperSet instance
79860 */
79861 public function getHelperSet()
79862 {
79863 return $this->helperSet;
79864 }
79865
79866 /**
79867 * Gets the application instance for this command.
79868 *
79869 * @return Application An Application instance
79870 *
79871 * @api
79872 */
79873 public function getApplication()
79874 {
79875 return $this->application;
79876 }
79877
79878 /**
79879 * Checks whether the command is enabled or not in the current environment
79880 *
79881 * Override this to check for x or y and return false if the command can not
79882 * run properly under the current conditions.
79883 *
79884 * @return Boolean
79885 */
79886 public function isEnabled()
79887 {
79888 return true;
79889 }
79890
79891 /**
79892 * Configures the current command.
79893 */
79894 protected function configure()
79895 {
79896 }
79897
79898 /**
79899 * Executes the current command.
79900 *
79901 * This method is not abstract because you can use this class
79902 * as a concrete class. In this case, instead of defining the
79903 * execute() method, you set the code to execute by passing
79904 * a Closure to the setCode() method.
79905 *
79906 * @param InputInterface $input An InputInterface instance
79907 * @param OutputInterface $output An OutputInterface instance
79908 *
79909 * @return null|integer null or 0 if everything went fine, or an error code
79910 *
79911 * @throws \LogicException When this abstract method is not implemented
79912 * @see setCode()
79913 */
79914 protected function execute(InputInterface $input, OutputInterface $output)
79915 {
79916 throw new \LogicException('You must override the execute() method in the concrete command class.');
79917 }
79918
79919 /**
79920 * Interacts with the user.
79921 *
79922 * @param InputInterface $input An InputInterface instance
79923 * @param OutputInterface $output An OutputInterface instance
79924 */
79925 protected function interact(InputInterface $input, OutputInterface $output)
79926 {
79927 }
79928
79929 /**
79930 * Initializes the command just after the input has been validated.
79931 *
79932 * This is mainly useful when a lot of commands extends one main command
79933 * where some things need to be initialized based on the input arguments and options.
79934 *
79935 * @param InputInterface $input An InputInterface instance
79936 * @param OutputInterface $output An OutputInterface instance
79937 */
79938 protected function initialize(InputInterface $input, OutputInterface $output)
79939 {
79940 }
79941
79942 /**
79943 * Runs the command.
79944 *
79945 * The code to execute is either defined directly with the
79946 * setCode() method or by overriding the execute() method
79947 * in a sub-class.
79948 *
79949 * @param InputInterface $input An InputInterface instance
79950 * @param OutputInterface $output An OutputInterface instance
79951 *
79952 * @return integer The command exit code
79953 *
79954 * @throws \Exception
79955 *
79956 * @see setCode()
79957 * @see execute()
79958 *
79959 * @api
79960 */
79961 public function run(InputInterface $input, OutputInterface $output)
79962 {
79963 // force the creation of the synopsis before the merge with the app definition
79964 $this->getSynopsis();
79965
79966 // add the application arguments and options
79967 $this->mergeApplicationDefinition();
79968
79969 // bind the input against the command specific arguments/options
79970 try {
79971 $input->bind($this->definition);
79972 } catch (\Exception $e) {
79973 if (!$this->ignoreValidationErrors) {
79974 throw $e;
79975 }
79976 }
79977
79978 $this->initialize($input, $output);
79979
79980 if ($input->isInteractive()) {
79981 $this->interact($input, $output);
79982 }
79983
79984 $input->validate();
79985
79986 if ($this->code) {
79987 $statusCode = call_user_func($this->code, $input, $output);
79988 } else {
79989 $statusCode = $this->execute($input, $output);
79990 }
79991
79992 return is_numeric($statusCode) ? (int) $statusCode : 0;
79993 }
79994
79995 /**
79996 * Sets the code to execute when running this command.
79997 *
79998 * If this method is used, it overrides the code defined
79999 * in the execute() method.
80000 *
80001 * @param callable $code A callable(InputInterface $input, OutputInterface $output)
80002 *
80003 * @return Command The current instance
80004 *
80005 * @throws \InvalidArgumentException
80006 *
80007 * @see execute()
80008 *
80009 * @api
80010 */
80011 public function setCode($code)
80012 {
80013 if (!is_callable($code)) {
80014 throw new \InvalidArgumentException('Invalid callable provided to Command::setCode.');
80015 }
80016
80017 $this->code = $code;
80018
80019 return $this;
80020 }
80021
80022 /**
80023 * Merges the application definition with the command definition.
80024 *
80025 * This method is not part of public API and should not be used directly.
80026 *
80027 * @param Boolean $mergeArgs Whether to merge or not the Application definition arguments to Command definition arguments
80028 */
80029 public function mergeApplicationDefinition($mergeArgs = true)
80030 {
80031 if (null === $this->application || (true === $this->applicationDefinitionMerged && ($this->applicationDefinitionMergedWithArgs || !$mergeArgs))) {
80032 return;
80033 }
80034
80035 if ($mergeArgs) {
80036 $currentArguments = $this->definition->getArguments();
80037 $this->definition->setArguments($this->application->getDefinition()->getArguments());
80038 $this->definition->addArguments($currentArguments);
80039 }
80040
80041 $this->definition->addOptions($this->application->getDefinition()->getOptions());
80042
80043 $this->applicationDefinitionMerged = true;
80044 if ($mergeArgs) {
80045 $this->applicationDefinitionMergedWithArgs = true;
80046 }
80047 }
80048
80049 /**
80050 * Sets an array of argument and option instances.
80051 *
80052 * @param array|InputDefinition $definition An array of argument and option instances or a definition instance
80053 *
80054 * @return Command The current instance
80055 *
80056 * @api
80057 */
80058 public function setDefinition($definition)
80059 {
80060 if ($definition instanceof InputDefinition) {
80061 $this->definition = $definition;
80062 } else {
80063 $this->definition->setDefinition($definition);
80064 }
80065
80066 $this->applicationDefinitionMerged = false;
80067
80068 return $this;
80069 }
80070
80071 /**
80072 * Gets the InputDefinition attached to this Command.
80073 *
80074 * @return InputDefinition An InputDefinition instance
80075 *
80076 * @api
80077 */
80078 public function getDefinition()
80079 {
80080 return $this->definition;
80081 }
80082
80083 /**
80084 * Gets the InputDefinition to be used to create XML and Text representations of this Command.
80085 *
80086 * Can be overridden to provide the original command representation when it would otherwise
80087 * be changed by merging with the application InputDefinition.
80088 *
80089 * This method is not part of public API and should not be used directly.
80090 *
80091 * @return InputDefinition An InputDefinition instance
80092 */
80093 public function getNativeDefinition()
80094 {
80095 return $this->getDefinition();
80096 }
80097
80098 /**
80099 * Adds an argument.
80100 *
80101 * @param string $name The argument name
80102 * @param integer $mode The argument mode: InputArgument::REQUIRED or InputArgument::OPTIONAL
80103 * @param string $description A description text
80104 * @param mixed $default The default value (for InputArgument::OPTIONAL mode only)
80105 *
80106 * @return Command The current instance
80107 *
80108 * @api
80109 */
80110 public function addArgument($name, $mode = null, $description = '', $default = null)
80111 {
80112 $this->definition->addArgument(new InputArgument($name, $mode, $description, $default));
80113
80114 return $this;
80115 }
80116
80117 /**
80118 * Adds an option.
80119 *
80120 * @param string $name The option name
80121 * @param string $shortcut The shortcut (can be null)
80122 * @param integer $mode The option mode: One of the InputOption::VALUE_* constants
80123 * @param string $description A description text
80124 * @param mixed $default The default value (must be null for InputOption::VALUE_REQUIRED or InputOption::VALUE_NONE)
80125 *
80126 * @return Command The current instance
80127 *
80128 * @api
80129 */
80130 public function addOption($name, $shortcut = null, $mode = null, $description = '', $default = null)
80131 {
80132 $this->definition->addOption(new InputOption($name, $shortcut, $mode, $description, $default));
80133
80134 return $this;
80135 }
80136
80137 /**
80138 * Sets the name of the command.
80139 *
80140 * This method can set both the namespace and the name if
80141 * you separate them by a colon (:)
80142 *
80143 * $command->setName('foo:bar');
80144 *
80145 * @param string $name The command name
80146 *
80147 * @return Command The current instance
80148 *
80149 * @throws \InvalidArgumentException When command name given is empty
80150 *
80151 * @api
80152 */
80153 public function setName($name)
80154 {
80155 $this->validateName($name);
80156
80157 $this->name = $name;
80158
80159 return $this;
80160 }
80161
80162 /**
80163 * Returns the command name.
80164 *
80165 * @return string The command name
80166 *
80167 * @api
80168 */
80169 public function getName()
80170 {
80171 return $this->name;
80172 }
80173
80174 /**
80175 * Sets the description for the command.
80176 *
80177 * @param string $description The description for the command
80178 *
80179 * @return Command The current instance
80180 *
80181 * @api
80182 */
80183 public function setDescription($description)
80184 {
80185 $this->description = $description;
80186
80187 return $this;
80188 }
80189
80190 /**
80191 * Returns the description for the command.
80192 *
80193 * @return string The description for the command
80194 *
80195 * @api
80196 */
80197 public function getDescription()
80198 {
80199 return $this->description;
80200 }
80201
80202 /**
80203 * Sets the help for the command.
80204 *
80205 * @param string $help The help for the command
80206 *
80207 * @return Command The current instance
80208 *
80209 * @api
80210 */
80211 public function setHelp($help)
80212 {
80213 $this->help = $help;
80214
80215 return $this;
80216 }
80217
80218 /**
80219 * Returns the help for the command.
80220 *
80221 * @return string The help for the command
80222 *
80223 * @api
80224 */
80225 public function getHelp()
80226 {
80227 return $this->help;
80228 }
80229
80230 /**
80231 * Returns the processed help for the command replacing the %command.name% and
80232 * %command.full_name% patterns with the real values dynamically.
80233 *
80234 * @return string The processed help for the command
80235 */
80236 public function getProcessedHelp()
80237 {
80238 $name = $this->name;
80239
80240 $placeholders = array(
80241 '%command.name%',
80242 '%command.full_name%'
80243 );
80244 $replacements = array(
80245 $name,
80246 $_SERVER['PHP_SELF'].' '.$name
80247 );
80248
80249 return str_replace($placeholders, $replacements, $this->getHelp());
80250 }
80251
80252 /**
80253 * Sets the aliases for the command.
80254 *
80255 * @param array $aliases An array of aliases for the command
80256 *
80257 * @return Command The current instance
80258 *
80259 * @api
80260 */
80261 public function setAliases($aliases)
80262 {
80263 foreach ($aliases as $alias) {
80264 $this->validateName($alias);
80265 }
80266
80267 $this->aliases = $aliases;
80268
80269 return $this;
80270 }
80271
80272 /**
80273 * Returns the aliases for the command.
80274 *
80275 * @return array An array of aliases for the command
80276 *
80277 * @api
80278 */
80279 public function getAliases()
80280 {
80281 return $this->aliases;
80282 }
80283
80284 /**
80285 * Returns the synopsis for the command.
80286 *
80287 * @return string The synopsis
80288 */
80289 public function getSynopsis()
80290 {
80291 if (null === $this->synopsis) {
80292 $this->synopsis = trim(sprintf('%s %s', $this->name, $this->definition->getSynopsis()));
80293 }
80294
80295 return $this->synopsis;
80296 }
80297
80298 /**
80299 * Gets a helper instance by name.
80300 *
80301 * @param string $name The helper name
80302 *
80303 * @return mixed The helper value
80304 *
80305 * @throws \InvalidArgumentException if the helper is not defined
80306 *
80307 * @api
80308 */
80309 public function getHelper($name)
80310 {
80311 return $this->helperSet->get($name);
80312 }
80313
80314 /**
80315 * Returns a text representation of the command.
80316 *
80317 * @return string A string representing the command
80318 *
80319 * @deprecated Deprecated since version 2.3, to be removed in 3.0.
80320 */
80321 public function asText()
80322 {
80323 $descriptor = new TextDescriptor();
80324
80325 return $descriptor->describe($this);
80326 }
80327
80328 /**
80329 * Returns an XML representation of the command.
80330 *
80331 * @param Boolean $asDom Whether to return a DOM or an XML string
80332 *
80333 * @return string|\DOMDocument An XML string representing the command
80334 *
80335 * @deprecated Deprecated since version 2.3, to be removed in 3.0.
80336 */
80337 public function asXml($asDom = false)
80338 {
80339 $descriptor = new XmlDescriptor();
80340
80341 return $descriptor->describe($this, array('as_dom' => $asDom));
80342 }
80343
80344 private function validateName($name)
80345 {
80346 if (!preg_match('/^[^\:]+(\:[^\:]+)*$/', $name)) {
80347 throw new \InvalidArgumentException(sprintf('Command name "%s" is invalid.', $name));
80348 }
80349 }
80350 }
80351 <?php
80352
80353 /*
80354 * This file is part of the Symfony package.
80355 *
80356 * (c) Fabien Potencier <fabien@symfony.com>
80357 *
80358 * For the full copyright and license information, please view the LICENSE
80359 * file that was distributed with this source code.
80360 */
80361
80362 namespace Symfony\Component\Console\Command;
80363
80364 use Symfony\Component\Console\Helper\DescriptorHelper;
80365 use Symfony\Component\Console\Input\InputArgument;
80366 use Symfony\Component\Console\Input\InputOption;
80367 use Symfony\Component\Console\Input\InputInterface;
80368 use Symfony\Component\Console\Output\OutputInterface;
80369
80370 /**
80371 * HelpCommand displays the help for a given command.
80372 *
80373 * @author Fabien Potencier <fabien@symfony.com>
80374 */
80375 class HelpCommand extends Command
80376 {
80377 private $command;
80378
80379 /**
80380 * {@inheritdoc}
80381 */
80382 protected function configure()
80383 {
80384 $this->ignoreValidationErrors();
80385
80386 $this
80387 ->setName('help')
80388 ->setDefinition(array(
80389 new InputArgument('command_name', InputArgument::OPTIONAL, 'The command name', 'help'),
80390 new InputOption('xml', null, InputOption::VALUE_NONE, 'To output help as XML'),
80391 new InputOption('format', null, InputOption::VALUE_REQUIRED, 'To output help in other formats'),
80392 new InputOption('raw', null, InputOption::VALUE_NONE, 'To output raw command help'),
80393 ))
80394 ->setDescription('Displays help for a command')
80395 ->setHelp(<<<EOF
80396 The <info>%command.name%</info> command displays help for a given command:
80397
80398 <info>php %command.full_name% list</info>
80399
80400 You can also output the help in other formats by using the <comment>--format</comment> option:
80401
80402 <info>php %command.full_name% --format=xml list</info>
80403
80404 To display the list of available commands, please use the <info>list</info> command.
80405 EOF
80406 )
80407 ;
80408 }
80409
80410 /**
80411 * Sets the command
80412 *
80413 * @param Command $command The command to set
80414 */
80415 public function setCommand(Command $command)
80416 {
80417 $this->command = $command;
80418 }
80419
80420 /**
80421 * {@inheritdoc}
80422 */
80423 protected function execute(InputInterface $input, OutputInterface $output)
80424 {
80425 if (null === $this->command) {
80426 $this->command = $this->getApplication()->find($input->getArgument('command_name'));
80427 }
80428
80429 if ($input->getOption('xml')) {
80430 $input->setOption('format', 'xml');
80431 }
80432
80433 $helper = new DescriptorHelper();
80434 $helper->describe($output, $this->command, $input->getOption('format'), $input->getOption('raw'));
80435 $this->command = null;
80436 }
80437 }
80438 <?php
80439
80440 /*
80441 * This file is part of the Symfony package.
80442 *
80443 * (c) Fabien Potencier <fabien@symfony.com>
80444 *
80445 * For the full copyright and license information, please view the LICENSE
80446 * file that was distributed with this source code.
80447 */
80448
80449 namespace Symfony\Component\Console\Tester;
80450
80451 use Symfony\Component\Console\Application;
80452 use Symfony\Component\Console\Input\ArrayInput;
80453 use Symfony\Component\Console\Input\InputInterface;
80454 use Symfony\Component\Console\Output\OutputInterface;
80455 use Symfony\Component\Console\Output\StreamOutput;
80456
80457 /**
80458 * Eases the testing of console applications.
80459 *
80460 * When testing an application, don't forget to disable the auto exit flag:
80461 *
80462 * $application = new Application();
80463 * $application->setAutoExit(false);
80464 *
80465 * @author Fabien Potencier <fabien@symfony.com>
80466 */
80467 class ApplicationTester
80468 {
80469 private $application;
80470 private $input;
80471 private $output;
80472
80473 /**
80474 * Constructor.
80475 *
80476 * @param Application $application An Application instance to test.
80477 */
80478 public function __construct(Application $application)
80479 {
80480 $this->application = $application;
80481 }
80482
80483 /**
80484 * Executes the application.
80485 *
80486 * Available options:
80487 *
80488 * * interactive: Sets the input interactive flag
80489 * * decorated: Sets the output decorated flag
80490 * * verbosity: Sets the output verbosity flag
80491 *
80492 * @param array $input An array of arguments and options
80493 * @param array $options An array of options
80494 *
80495 * @return integer The command exit code
80496 */
80497 public function run(array $input, $options = array())
80498 {
80499 $this->input = new ArrayInput($input);
80500 if (isset($options['interactive'])) {
80501 $this->input->setInteractive($options['interactive']);
80502 }
80503
80504 $this->output = new StreamOutput(fopen('php://memory', 'w', false));
80505 if (isset($options['decorated'])) {
80506 $this->output->setDecorated($options['decorated']);
80507 }
80508 if (isset($options['verbosity'])) {
80509 $this->output->setVerbosity($options['verbosity']);
80510 }
80511
80512 return $this->application->run($this->input, $this->output);
80513 }
80514
80515 /**
80516 * Gets the display returned by the last execution of the application.
80517 *
80518 * @param Boolean $normalize Whether to normalize end of lines to \n or not
80519 *
80520 * @return string The display
80521 */
80522 public function getDisplay($normalize = false)
80523 {
80524 rewind($this->output->getStream());
80525
80526 $display = stream_get_contents($this->output->getStream());
80527
80528 if ($normalize) {
80529 $display = str_replace(PHP_EOL, "\n", $display);
80530 }
80531
80532 return $display;
80533 }
80534
80535 /**
80536 * Gets the input instance used by the last execution of the application.
80537 *
80538 * @return InputInterface The current input instance
80539 */
80540 public function getInput()
80541 {
80542 return $this->input;
80543 }
80544
80545 /**
80546 * Gets the output instance used by the last execution of the application.
80547 *
80548 * @return OutputInterface The current output instance
80549 */
80550 public function getOutput()
80551 {
80552 return $this->output;
80553 }
80554 }
80555 <?php
80556
80557 /*
80558 * This file is part of the Symfony package.
80559 *
80560 * (c) Fabien Potencier <fabien@symfony.com>
80561 *
80562 * For the full copyright and license information, please view the LICENSE
80563 * file that was distributed with this source code.
80564 */
80565
80566 namespace Symfony\Component\Console\Tester;
80567
80568 use Symfony\Component\Console\Command\Command;
80569 use Symfony\Component\Console\Input\ArrayInput;
80570 use Symfony\Component\Console\Output\StreamOutput;
80571
80572 /**
80573 * Eases the testing of console commands.
80574 *
80575 * @author Fabien Potencier <fabien@symfony.com>
80576 */
80577 class CommandTester
80578 {
80579 private $command;
80580 private $input;
80581 private $output;
80582
80583 /**
80584 * Constructor.
80585 *
80586 * @param Command $command A Command instance to test.
80587 */
80588 public function __construct(Command $command)
80589 {
80590 $this->command = $command;
80591 }
80592
80593 /**
80594 * Executes the command.
80595 *
80596 * Available options:
80597 *
80598 * * interactive: Sets the input interactive flag
80599 * * decorated: Sets the output decorated flag
80600 * * verbosity: Sets the output verbosity flag
80601 *
80602 * @param array $input An array of arguments and options
80603 * @param array $options An array of options
80604 *
80605 * @return integer The command exit code
80606 */
80607 public function execute(array $input, array $options = array())
80608 {
80609 $this->input = new ArrayInput($input);
80610 if (isset($options['interactive'])) {
80611 $this->input->setInteractive($options['interactive']);
80612 }
80613
80614 $this->output = new StreamOutput(fopen('php://memory', 'w', false));
80615 if (isset($options['decorated'])) {
80616 $this->output->setDecorated($options['decorated']);
80617 }
80618 if (isset($options['verbosity'])) {
80619 $this->output->setVerbosity($options['verbosity']);
80620 }
80621
80622 return $this->command->run($this->input, $this->output);
80623 }
80624
80625 /**
80626 * Gets the display returned by the last execution of the command.
80627 *
80628 * @param Boolean $normalize Whether to normalize end of lines to \n or not
80629 *
80630 * @return string The display
80631 */
80632 public function getDisplay($normalize = false)
80633 {
80634 rewind($this->output->getStream());
80635
80636 $display = stream_get_contents($this->output->getStream());
80637
80638 if ($normalize) {
80639 $display = str_replace(PHP_EOL, "\n", $display);
80640 }
80641
80642 return $display;
80643 }
80644
80645 /**
80646 * Gets the input instance used by the last execution of the command.
80647 *
80648 * @return InputInterface The current input instance
80649 */
80650 public function getInput()
80651 {
80652 return $this->input;
80653 }
80654
80655 /**
80656 * Gets the output instance used by the last execution of the command.
80657 *
80658 * @return OutputInterface The current output instance
80659 */
80660 public function getOutput()
80661 {
80662 return $this->output;
80663 }
80664 }
80665 <?php
80666
80667 /*
80668 * This file is part of the Symfony package.
80669 *
80670 * (c) Fabien Potencier <fabien@symfony.com>
80671 *
80672 * For the full copyright and license information, please view the LICENSE
80673 * file that was distributed with this source code.
80674 */
80675
80676 namespace Symfony\Component\Console\Input;
80677
80678 /**
80679 * StringInput represents an input provided as a string.
80680 *
80681 * Usage:
80682 *
80683 * $input = new StringInput('foo --bar="foobar"');
80684 *
80685 * @author Fabien Potencier <fabien@symfony.com>
80686 *
80687 * @api
80688 */
80689 class StringInput extends ArgvInput
80690 {
80691 const REGEX_STRING = '([^\s]+?)(?:\s|(?<!\\\\)"|(?<!\\\\)\'|$)';
80692 const REGEX_QUOTED_STRING = '(?:"([^"\\\\]*(?:\\\\.[^"\\\\]*)*)"|\'([^\'\\\\]*(?:\\\\.[^\'\\\\]*)*)\')';
80693
80694 /**
80695 * Constructor.
80696 *
80697 * @param string $input An array of parameters from the CLI (in the argv format)
80698 * @param InputDefinition $definition A InputDefinition instance
80699 *
80700 * @deprecated The second argument is deprecated as it does not work (will be removed in 3.0), use 'bind' method instead
80701 *
80702 * @api
80703 */
80704 public function __construct($input, InputDefinition $definition = null)
80705 {
80706 parent::__construct(array(), null);
80707
80708 $this->setTokens($this->tokenize($input));
80709
80710 if (null !== $definition) {
80711 $this->bind($definition);
80712 }
80713 }
80714
80715 /**
80716 * Tokenizes a string.
80717 *
80718 * @param string $input The input to tokenize
80719 *
80720 * @return array An array of tokens
80721 *
80722 * @throws \InvalidArgumentException When unable to parse input (should never happen)
80723 */
80724 private function tokenize($input)
80725 {
80726 $tokens = array();
80727 $length = strlen($input);
80728 $cursor = 0;
80729 while ($cursor < $length) {
80730 if (preg_match('/\s+/A', $input, $match, null, $cursor)) {
80731 } elseif (preg_match('/([^="\'\s]+?)(=?)('.self::REGEX_QUOTED_STRING.'+)/A', $input, $match, null, $cursor)) {
80732 $tokens[] = $match[1].$match[2].stripcslashes(str_replace(array('"\'', '\'"', '\'\'', '""'), '', substr($match[3], 1, strlen($match[3]) - 2)));
80733 } elseif (preg_match('/'.self::REGEX_QUOTED_STRING.'/A', $input, $match, null, $cursor)) {
80734 $tokens[] = stripcslashes(substr($match[0], 1, strlen($match[0]) - 2));
80735 } elseif (preg_match('/'.self::REGEX_STRING.'/A', $input, $match, null, $cursor)) {
80736 $tokens[] = stripcslashes($match[1]);
80737 } else {
80738 // should never happen
80739 // @codeCoverageIgnoreStart
80740 throw new \InvalidArgumentException(sprintf('Unable to parse input near "... %s ..."', substr($input, $cursor, 10)));
80741 // @codeCoverageIgnoreEnd
80742 }
80743
80744 $cursor += strlen($match[0]);
80745 }
80746
80747 return $tokens;
80748 }
80749 }
80750 <?php
80751
80752 /*
80753 * This file is part of the Symfony package.
80754 *
80755 * (c) Fabien Potencier <fabien@symfony.com>
80756 *
80757 * For the full copyright and license information, please view the LICENSE
80758 * file that was distributed with this source code.
80759 */
80760
80761 namespace Symfony\Component\Console\Input;
80762
80763 /**
80764 * Represents a command line argument.
80765 *
80766 * @author Fabien Potencier <fabien@symfony.com>
80767 *
80768 * @api
80769 */
80770 class InputArgument
80771 {
80772 const REQUIRED = 1;
80773 const OPTIONAL = 2;
80774 const IS_ARRAY = 4;
80775
80776 private $name;
80777 private $mode;
80778 private $default;
80779 private $description;
80780
80781 /**
80782 * Constructor.
80783 *
80784 * @param string $name The argument name
80785 * @param integer $mode The argument mode: self::REQUIRED or self::OPTIONAL
80786 * @param string $description A description text
80787 * @param mixed $default The default value (for self::OPTIONAL mode only)
80788 *
80789 * @throws \InvalidArgumentException When argument mode is not valid
80790 *
80791 * @api
80792 */
80793 public function __construct($name, $mode = null, $description = '', $default = null)
80794 {
80795 if (null === $mode) {
80796 $mode = self::OPTIONAL;
80797 } elseif (!is_int($mode) || $mode > 7 || $mode < 1) {
80798 throw new \InvalidArgumentException(sprintf('Argument mode "%s" is not valid.', $mode));
80799 }
80800
80801 $this->name = $name;
80802 $this->mode = $mode;
80803 $this->description = $description;
80804
80805 $this->setDefault($default);
80806 }
80807
80808 /**
80809 * Returns the argument name.
80810 *
80811 * @return string The argument name
80812 */
80813 public function getName()
80814 {
80815 return $this->name;
80816 }
80817
80818 /**
80819 * Returns true if the argument is required.
80820 *
80821 * @return Boolean true if parameter mode is self::REQUIRED, false otherwise
80822 */
80823 public function isRequired()
80824 {
80825 return self::REQUIRED === (self::REQUIRED & $this->mode);
80826 }
80827
80828 /**
80829 * Returns true if the argument can take multiple values.
80830 *
80831 * @return Boolean true if mode is self::IS_ARRAY, false otherwise
80832 */
80833 public function isArray()
80834 {
80835 return self::IS_ARRAY === (self::IS_ARRAY & $this->mode);
80836 }
80837
80838 /**
80839 * Sets the default value.
80840 *
80841 * @param mixed $default The default value
80842 *
80843 * @throws \LogicException When incorrect default value is given
80844 */
80845 public function setDefault($default = null)
80846 {
80847 if (self::REQUIRED === $this->mode && null !== $default) {
80848 throw new \LogicException('Cannot set a default value except for InputArgument::OPTIONAL mode.');
80849 }
80850
80851 if ($this->isArray()) {
80852 if (null === $default) {
80853 $default = array();
80854 } elseif (!is_array($default)) {
80855 throw new \LogicException('A default value for an array argument must be an array.');
80856 }
80857 }
80858
80859 $this->default = $default;
80860 }
80861
80862 /**
80863 * Returns the default value.
80864 *
80865 * @return mixed The default value
80866 */
80867 public function getDefault()
80868 {
80869 return $this->default;
80870 }
80871
80872 /**
80873 * Returns the description text.
80874 *
80875 * @return string The description text
80876 */
80877 public function getDescription()
80878 {
80879 return $this->description;
80880 }
80881 }
80882 <?php
80883
80884 /*
80885 * This file is part of the Symfony package.
80886 *
80887 * (c) Fabien Potencier <fabien@symfony.com>
80888 *
80889 * For the full copyright and license information, please view the LICENSE
80890 * file that was distributed with this source code.
80891 */
80892
80893 namespace Symfony\Component\Console\Input;
80894
80895 /**
80896 * ArrayInput represents an input provided as an array.
80897 *
80898 * Usage:
80899 *
80900 * $input = new ArrayInput(array('name' => 'foo', '--bar' => 'foobar'));
80901 *
80902 * @author Fabien Potencier <fabien@symfony.com>
80903 *
80904 * @api
80905 */
80906 class ArrayInput extends Input
80907 {
80908 private $parameters;
80909
80910 /**
80911 * Constructor.
80912 *
80913 * @param array $parameters An array of parameters
80914 * @param InputDefinition $definition A InputDefinition instance
80915 *
80916 * @api
80917 */
80918 public function __construct(array $parameters, InputDefinition $definition = null)
80919 {
80920 $this->parameters = $parameters;
80921
80922 parent::__construct($definition);
80923 }
80924
80925 /**
80926 * Returns the first argument from the raw parameters (not parsed).
80927 *
80928 * @return string The value of the first argument or null otherwise
80929 */
80930 public function getFirstArgument()
80931 {
80932 foreach ($this->parameters as $key => $value) {
80933 if ($key && '-' === $key[0]) {
80934 continue;
80935 }
80936
80937 return $value;
80938 }
80939 }
80940
80941 /**
80942 * Returns true if the raw parameters (not parsed) contain a value.
80943 *
80944 * This method is to be used to introspect the input parameters
80945 * before they have been validated. It must be used carefully.
80946 *
80947 * @param string|array $values The values to look for in the raw parameters (can be an array)
80948 *
80949 * @return Boolean true if the value is contained in the raw parameters
80950 */
80951 public function hasParameterOption($values)
80952 {
80953 $values = (array) $values;
80954
80955 foreach ($this->parameters as $k => $v) {
80956 if (!is_int($k)) {
80957 $v = $k;
80958 }
80959
80960 if (in_array($v, $values)) {
80961 return true;
80962 }
80963 }
80964
80965 return false;
80966 }
80967
80968 /**
80969 * Returns the value of a raw option (not parsed).
80970 *
80971 * This method is to be used to introspect the input parameters
80972 * before they have been validated. It must be used carefully.
80973 *
80974 * @param string|array $values The value(s) to look for in the raw parameters (can be an array)
80975 * @param mixed $default The default value to return if no result is found
80976 *
80977 * @return mixed The option value
80978 */
80979 public function getParameterOption($values, $default = false)
80980 {
80981 $values = (array) $values;
80982
80983 foreach ($this->parameters as $k => $v) {
80984 if (is_int($k) && in_array($v, $values)) {
80985 return true;
80986 } elseif (in_array($k, $values)) {
80987 return $v;
80988 }
80989 }
80990
80991 return $default;
80992 }
80993
80994 /**
80995 * Returns a stringified representation of the args passed to the command
80996 *
80997 * @return string
80998 */
80999 public function __toString()
81000 {
81001 $params = array();
81002 foreach ($this->parameters as $param => $val) {
81003 if ($param && '-' === $param[0]) {
81004 $params[] = $param . ('' != $val ? '='.$this->escapeToken($val) : '');
81005 } else {
81006 $params[] = $this->escapeToken($val);
81007 }
81008 }
81009
81010 return implode(' ', $params);
81011 }
81012
81013 /**
81014 * Processes command line arguments.
81015 */
81016 protected function parse()
81017 {
81018 foreach ($this->parameters as $key => $value) {
81019 if (0 === strpos($key, '--')) {
81020 $this->addLongOption(substr($key, 2), $value);
81021 } elseif ('-' === $key[0]) {
81022 $this->addShortOption(substr($key, 1), $value);
81023 } else {
81024 $this->addArgument($key, $value);
81025 }
81026 }
81027 }
81028
81029 /**
81030 * Adds a short option value.
81031 *
81032 * @param string $shortcut The short option key
81033 * @param mixed $value The value for the option
81034 *
81035 * @throws \InvalidArgumentException When option given doesn't exist
81036 */
81037 private function addShortOption($shortcut, $value)
81038 {
81039 if (!$this->definition->hasShortcut($shortcut)) {
81040 throw new \InvalidArgumentException(sprintf('The "-%s" option does not exist.', $shortcut));
81041 }
81042
81043 $this->addLongOption($this->definition->getOptionForShortcut($shortcut)->getName(), $value);
81044 }
81045
81046 /**
81047 * Adds a long option value.
81048 *
81049 * @param string $name The long option key
81050 * @param mixed $value The value for the option
81051 *
81052 * @throws \InvalidArgumentException When option given doesn't exist
81053 * @throws \InvalidArgumentException When a required value is missing
81054 */
81055 private function addLongOption($name, $value)
81056 {
81057 if (!$this->definition->hasOption($name)) {
81058 throw new \InvalidArgumentException(sprintf('The "--%s" option does not exist.', $name));
81059 }
81060
81061 $option = $this->definition->getOption($name);
81062
81063 if (null === $value) {
81064 if ($option->isValueRequired()) {
81065 throw new \InvalidArgumentException(sprintf('The "--%s" option requires a value.', $name));
81066 }
81067
81068 $value = $option->isValueOptional() ? $option->getDefault() : true;
81069 }
81070
81071 $this->options[$name] = $value;
81072 }
81073
81074 /**
81075 * Adds an argument value.
81076 *
81077 * @param string $name The argument name
81078 * @param mixed $value The value for the argument
81079 *
81080 * @throws \InvalidArgumentException When argument given doesn't exist
81081 */
81082 private function addArgument($name, $value)
81083 {
81084 if (!$this->definition->hasArgument($name)) {
81085 throw new \InvalidArgumentException(sprintf('The "%s" argument does not exist.', $name));
81086 }
81087
81088 $this->arguments[$name] = $value;
81089 }
81090 }
81091 <?php
81092
81093 /*
81094 * This file is part of the Symfony package.
81095 *
81096 * (c) Fabien Potencier <fabien@symfony.com>
81097 *
81098 * For the full copyright and license information, please view the LICENSE
81099 * file that was distributed with this source code.
81100 */
81101
81102 namespace Symfony\Component\Console\Input;
81103
81104 /**
81105 * Input is the base class for all concrete Input classes.
81106 *
81107 * Three concrete classes are provided by default:
81108 *
81109 * * `ArgvInput`: The input comes from the CLI arguments (argv)
81110 * * `StringInput`: The input is provided as a string
81111 * * `ArrayInput`: The input is provided as an array
81112 *
81113 * @author Fabien Potencier <fabien@symfony.com>
81114 */
81115 abstract class Input implements InputInterface
81116 {
81117 protected $definition;
81118 protected $options;
81119 protected $arguments;
81120 protected $interactive = true;
81121
81122 /**
81123 * Constructor.
81124 *
81125 * @param InputDefinition $definition A InputDefinition instance
81126 */
81127 public function __construct(InputDefinition $definition = null)
81128 {
81129 if (null === $definition) {
81130 $this->arguments = array();
81131 $this->options = array();
81132 $this->definition = new InputDefinition();
81133 } else {
81134 $this->bind($definition);
81135 $this->validate();
81136 }
81137 }
81138
81139 /**
81140 * Binds the current Input instance with the given arguments and options.
81141 *
81142 * @param InputDefinition $definition A InputDefinition instance
81143 */
81144 public function bind(InputDefinition $definition)
81145 {
81146 $this->arguments = array();
81147 $this->options = array();
81148 $this->definition = $definition;
81149
81150 $this->parse();
81151 }
81152
81153 /**
81154 * Processes command line arguments.
81155 */
81156 abstract protected function parse();
81157
81158 /**
81159 * Validates the input.
81160 *
81161 * @throws \RuntimeException When not enough arguments are given
81162 */
81163 public function validate()
81164 {
81165 if (count($this->arguments) < $this->definition->getArgumentRequiredCount()) {
81166 throw new \RuntimeException('Not enough arguments.');
81167 }
81168 }
81169
81170 /**
81171 * Checks if the input is interactive.
81172 *
81173 * @return Boolean Returns true if the input is interactive
81174 */
81175 public function isInteractive()
81176 {
81177 return $this->interactive;
81178 }
81179
81180 /**
81181 * Sets the input interactivity.
81182 *
81183 * @param Boolean $interactive If the input should be interactive
81184 */
81185 public function setInteractive($interactive)
81186 {
81187 $this->interactive = (Boolean) $interactive;
81188 }
81189
81190 /**
81191 * Returns the argument values.
81192 *
81193 * @return array An array of argument values
81194 */
81195 public function getArguments()
81196 {
81197 return array_merge($this->definition->getArgumentDefaults(), $this->arguments);
81198 }
81199
81200 /**
81201 * Returns the argument value for a given argument name.
81202 *
81203 * @param string $name The argument name
81204 *
81205 * @return mixed The argument value
81206 *
81207 * @throws \InvalidArgumentException When argument given doesn't exist
81208 */
81209 public function getArgument($name)
81210 {
81211 if (!$this->definition->hasArgument($name)) {
81212 throw new \InvalidArgumentException(sprintf('The "%s" argument does not exist.', $name));
81213 }
81214
81215 return isset($this->arguments[$name]) ? $this->arguments[$name] : $this->definition->getArgument($name)->getDefault();
81216 }
81217
81218 /**
81219 * Sets an argument value by name.
81220 *
81221 * @param string $name The argument name
81222 * @param string $value The argument value
81223 *
81224 * @throws \InvalidArgumentException When argument given doesn't exist
81225 */
81226 public function setArgument($name, $value)
81227 {
81228 if (!$this->definition->hasArgument($name)) {
81229 throw new \InvalidArgumentException(sprintf('The "%s" argument does not exist.', $name));
81230 }
81231
81232 $this->arguments[$name] = $value;
81233 }
81234
81235 /**
81236 * Returns true if an InputArgument object exists by name or position.
81237 *
81238 * @param string|integer $name The InputArgument name or position
81239 *
81240 * @return Boolean true if the InputArgument object exists, false otherwise
81241 */
81242 public function hasArgument($name)
81243 {
81244 return $this->definition->hasArgument($name);
81245 }
81246
81247 /**
81248 * Returns the options values.
81249 *
81250 * @return array An array of option values
81251 */
81252 public function getOptions()
81253 {
81254 return array_merge($this->definition->getOptionDefaults(), $this->options);
81255 }
81256
81257 /**
81258 * Returns the option value for a given option name.
81259 *
81260 * @param string $name The option name
81261 *
81262 * @return mixed The option value
81263 *
81264 * @throws \InvalidArgumentException When option given doesn't exist
81265 */
81266 public function getOption($name)
81267 {
81268 if (!$this->definition->hasOption($name)) {
81269 throw new \InvalidArgumentException(sprintf('The "%s" option does not exist.', $name));
81270 }
81271
81272 return isset($this->options[$name]) ? $this->options[$name] : $this->definition->getOption($name)->getDefault();
81273 }
81274
81275 /**
81276 * Sets an option value by name.
81277 *
81278 * @param string $name The option name
81279 * @param string $value The option value
81280 *
81281 * @throws \InvalidArgumentException When option given doesn't exist
81282 */
81283 public function setOption($name, $value)
81284 {
81285 if (!$this->definition->hasOption($name)) {
81286 throw new \InvalidArgumentException(sprintf('The "%s" option does not exist.', $name));
81287 }
81288
81289 $this->options[$name] = $value;
81290 }
81291
81292 /**
81293 * Returns true if an InputOption object exists by name.
81294 *
81295 * @param string $name The InputOption name
81296 *
81297 * @return Boolean true if the InputOption object exists, false otherwise
81298 */
81299 public function hasOption($name)
81300 {
81301 return $this->definition->hasOption($name);
81302 }
81303
81304 /**
81305 * Escapes a token through escapeshellarg if it contains unsafe chars
81306 *
81307 * @param string $token
81308 *
81309 * @return string
81310 */
81311 public function escapeToken($token)
81312 {
81313 return preg_match('{^[\w-]+$}', $token) ? $token : escapeshellarg($token);
81314 }
81315 }
81316 <?php
81317
81318 /*
81319 * This file is part of the Symfony package.
81320 *
81321 * (c) Fabien Potencier <fabien@symfony.com>
81322 *
81323 * For the full copyright and license information, please view the LICENSE
81324 * file that was distributed with this source code.
81325 */
81326
81327 namespace Symfony\Component\Console\Input;
81328
81329 /**
81330 * ArgvInput represents an input coming from the CLI arguments.
81331 *
81332 * Usage:
81333 *
81334 * $input = new ArgvInput();
81335 *
81336 * By default, the `$_SERVER['argv']` array is used for the input values.
81337 *
81338 * This can be overridden by explicitly passing the input values in the constructor:
81339 *
81340 * $input = new ArgvInput($_SERVER['argv']);
81341 *
81342 * If you pass it yourself, don't forget that the first element of the array
81343 * is the name of the running application.
81344 *
81345 * When passing an argument to the constructor, be sure that it respects
81346 * the same rules as the argv one. It's almost always better to use the
81347 * `StringInput` when you want to provide your own input.
81348 *
81349 * @author Fabien Potencier <fabien@symfony.com>
81350 *
81351 * @see http://www.gnu.org/software/libc/manual/html_node/Argument-Syntax.html
81352 * @see http://www.opengroup.org/onlinepubs/009695399/basedefs/xbd_chap12.html#tag_12_02
81353 *
81354 * @api
81355 */
81356 class ArgvInput extends Input
81357 {
81358 private $tokens;
81359 private $parsed;
81360
81361 /**
81362 * Constructor.
81363 *
81364 * @param array $argv An array of parameters from the CLI (in the argv format)
81365 * @param InputDefinition $definition A InputDefinition instance
81366 *
81367 * @api
81368 */
81369 public function __construct(array $argv = null, InputDefinition $definition = null)
81370 {
81371 if (null === $argv) {
81372 $argv = $_SERVER['argv'];
81373 }
81374
81375 // strip the application name
81376 array_shift($argv);
81377
81378 $this->tokens = $argv;
81379
81380 parent::__construct($definition);
81381 }
81382
81383 protected function setTokens(array $tokens)
81384 {
81385 $this->tokens = $tokens;
81386 }
81387
81388 /**
81389 * Processes command line arguments.
81390 */
81391 protected function parse()
81392 {
81393 $parseOptions = true;
81394 $this->parsed = $this->tokens;
81395 while (null !== $token = array_shift($this->parsed)) {
81396 if ($parseOptions && '' == $token) {
81397 $this->parseArgument($token);
81398 } elseif ($parseOptions && '--' == $token) {
81399 $parseOptions = false;
81400 } elseif ($parseOptions && 0 === strpos($token, '--')) {
81401 $this->parseLongOption($token);
81402 } elseif ($parseOptions && '-' === $token[0]) {
81403 $this->parseShortOption($token);
81404 } else {
81405 $this->parseArgument($token);
81406 }
81407 }
81408 }
81409
81410 /**
81411 * Parses a short option.
81412 *
81413 * @param string $token The current token.
81414 */
81415 private function parseShortOption($token)
81416 {
81417 $name = substr($token, 1);
81418
81419 if (strlen($name) > 1) {
81420 if ($this->definition->hasShortcut($name[0]) && $this->definition->getOptionForShortcut($name[0])->acceptValue()) {
81421 // an option with a value (with no space)
81422 $this->addShortOption($name[0], substr($name, 1));
81423 } else {
81424 $this->parseShortOptionSet($name);
81425 }
81426 } else {
81427 $this->addShortOption($name, null);
81428 }
81429 }
81430
81431 /**
81432 * Parses a short option set.
81433 *
81434 * @param string $name The current token
81435 *
81436 * @throws \RuntimeException When option given doesn't exist
81437 */
81438 private function parseShortOptionSet($name)
81439 {
81440 $len = strlen($name);
81441 for ($i = 0; $i < $len; $i++) {
81442 if (!$this->definition->hasShortcut($name[$i])) {
81443 throw new \RuntimeException(sprintf('The "-%s" option does not exist.', $name[$i]));
81444 }
81445
81446 $option = $this->definition->getOptionForShortcut($name[$i]);
81447 if ($option->acceptValue()) {
81448 $this->addLongOption($option->getName(), $i === $len - 1 ? null : substr($name, $i + 1));
81449
81450 break;
81451 } else {
81452 $this->addLongOption($option->getName(), null);
81453 }
81454 }
81455 }
81456
81457 /**
81458 * Parses a long option.
81459 *
81460 * @param string $token The current token
81461 */
81462 private function parseLongOption($token)
81463 {
81464 $name = substr($token, 2);
81465
81466 if (false !== $pos = strpos($name, '=')) {
81467 $this->addLongOption(substr($name, 0, $pos), substr($name, $pos + 1));
81468 } else {
81469 $this->addLongOption($name, null);
81470 }
81471 }
81472
81473 /**
81474 * Parses an argument.
81475 *
81476 * @param string $token The current token
81477 *
81478 * @throws \RuntimeException When too many arguments are given
81479 */
81480 private function parseArgument($token)
81481 {
81482 $c = count($this->arguments);
81483
81484 // if input is expecting another argument, add it
81485 if ($this->definition->hasArgument($c)) {
81486 $arg = $this->definition->getArgument($c);
81487 $this->arguments[$arg->getName()] = $arg->isArray()? array($token) : $token;
81488
81489 // if last argument isArray(), append token to last argument
81490 } elseif ($this->definition->hasArgument($c - 1) && $this->definition->getArgument($c - 1)->isArray()) {
81491 $arg = $this->definition->getArgument($c - 1);
81492 $this->arguments[$arg->getName()][] = $token;
81493
81494 // unexpected argument
81495 } else {
81496 throw new \RuntimeException('Too many arguments.');
81497 }
81498 }
81499
81500 /**
81501 * Adds a short option value.
81502 *
81503 * @param string $shortcut The short option key
81504 * @param mixed $value The value for the option
81505 *
81506 * @throws \RuntimeException When option given doesn't exist
81507 */
81508 private function addShortOption($shortcut, $value)
81509 {
81510 if (!$this->definition->hasShortcut($shortcut)) {
81511 throw new \RuntimeException(sprintf('The "-%s" option does not exist.', $shortcut));
81512 }
81513
81514 $this->addLongOption($this->definition->getOptionForShortcut($shortcut)->getName(), $value);
81515 }
81516
81517 /**
81518 * Adds a long option value.
81519 *
81520 * @param string $name The long option key
81521 * @param mixed $value The value for the option
81522 *
81523 * @throws \RuntimeException When option given doesn't exist
81524 */
81525 private function addLongOption($name, $value)
81526 {
81527 if (!$this->definition->hasOption($name)) {
81528 throw new \RuntimeException(sprintf('The "--%s" option does not exist.', $name));
81529 }
81530
81531 $option = $this->definition->getOption($name);
81532
81533 // Convert false values (from a previous call to substr()) to null
81534 if (false === $value) {
81535 $value = null;
81536 }
81537
81538 if (null !== $value && !$option->acceptValue()) {
81539 throw new \RuntimeException(sprintf('The "--%s" option does not accept a value.', $name, $value));
81540 }
81541
81542 if (null === $value && $option->acceptValue() && count($this->parsed)) {
81543 // if option accepts an optional or mandatory argument
81544 // let's see if there is one provided
81545 $next = array_shift($this->parsed);
81546 if (isset($next[0]) && '-' !== $next[0]) {
81547 $value = $next;
81548 } elseif (empty($next)) {
81549 $value = '';
81550 } else {
81551 array_unshift($this->parsed, $next);
81552 }
81553 }
81554
81555 if (null === $value) {
81556 if ($option->isValueRequired()) {
81557 throw new \RuntimeException(sprintf('The "--%s" option requires a value.', $name));
81558 }
81559
81560 if (!$option->isArray()) {
81561 $value = $option->isValueOptional() ? $option->getDefault() : true;
81562 }
81563 }
81564
81565 if ($option->isArray()) {
81566 $this->options[$name][] = $value;
81567 } else {
81568 $this->options[$name] = $value;
81569 }
81570 }
81571
81572 /**
81573 * Returns the first argument from the raw parameters (not parsed).
81574 *
81575 * @return string The value of the first argument or null otherwise
81576 */
81577 public function getFirstArgument()
81578 {
81579 foreach ($this->tokens as $token) {
81580 if ($token && '-' === $token[0]) {
81581 continue;
81582 }
81583
81584 return $token;
81585 }
81586 }
81587
81588 /**
81589 * Returns true if the raw parameters (not parsed) contain a value.
81590 *
81591 * This method is to be used to introspect the input parameters
81592 * before they have been validated. It must be used carefully.
81593 *
81594 * @param string|array $values The value(s) to look for in the raw parameters (can be an array)
81595 *
81596 * @return Boolean true if the value is contained in the raw parameters
81597 */
81598 public function hasParameterOption($values)
81599 {
81600 $values = (array) $values;
81601
81602 foreach ($this->tokens as $token) {
81603 foreach ($values as $value) {
81604 if ($token === $value || 0 === strpos($token, $value.'=')) {
81605 return true;
81606 }
81607 }
81608 }
81609
81610 return false;
81611 }
81612
81613 /**
81614 * Returns the value of a raw option (not parsed).
81615 *
81616 * This method is to be used to introspect the input parameters
81617 * before they have been validated. It must be used carefully.
81618 *
81619 * @param string|array $values The value(s) to look for in the raw parameters (can be an array)
81620 * @param mixed $default The default value to return if no result is found
81621 *
81622 * @return mixed The option value
81623 */
81624 public function getParameterOption($values, $default = false)
81625 {
81626 $values = (array) $values;
81627
81628 $tokens = $this->tokens;
81629 while ($token = array_shift($tokens)) {
81630 foreach ($values as $value) {
81631 if ($token === $value || 0 === strpos($token, $value.'=')) {
81632 if (false !== $pos = strpos($token, '=')) {
81633 return substr($token, $pos + 1);
81634 }
81635
81636 return array_shift($tokens);
81637 }
81638 }
81639 }
81640
81641 return $default;
81642 }
81643
81644 /**
81645 * Returns a stringified representation of the args passed to the command
81646 *
81647 * @return string
81648 */
81649 public function __toString()
81650 {
81651 $self = $this;
81652 $tokens = array_map(function ($token) use ($self) {
81653 if (preg_match('{^(-[^=]+=)(.+)}', $token, $match)) {
81654 return $match[1] . $self->escapeToken($match[2]);
81655 }
81656
81657 if ($token && $token[0] !== '-') {
81658 return $self->escapeToken($token);
81659 }
81660
81661 return $token;
81662 }, $this->tokens);
81663
81664 return implode(' ', $tokens);
81665 }
81666 }
81667 <?php
81668
81669 /*
81670 * This file is part of the Symfony package.
81671 *
81672 * (c) Fabien Potencier <fabien@symfony.com>
81673 *
81674 * For the full copyright and license information, please view the LICENSE
81675 * file that was distributed with this source code.
81676 */
81677
81678 namespace Symfony\Component\Console\Input;
81679
81680 /**
81681 * InputInterface is the interface implemented by all input classes.
81682 *
81683 * @author Fabien Potencier <fabien@symfony.com>
81684 */
81685 interface InputInterface
81686 {
81687 /**
81688 * Returns the first argument from the raw parameters (not parsed).
81689 *
81690 * @return string The value of the first argument or null otherwise
81691 */
81692 public function getFirstArgument();
81693
81694 /**
81695 * Returns true if the raw parameters (not parsed) contain a value.
81696 *
81697 * This method is to be used to introspect the input parameters
81698 * before they have been validated. It must be used carefully.
81699 *
81700 * @param string|array $values The values to look for in the raw parameters (can be an array)
81701 *
81702 * @return Boolean true if the value is contained in the raw parameters
81703 */
81704 public function hasParameterOption($values);
81705
81706 /**
81707 * Returns the value of a raw option (not parsed).
81708 *
81709 * This method is to be used to introspect the input parameters
81710 * before they have been validated. It must be used carefully.
81711 *
81712 * @param string|array $values The value(s) to look for in the raw parameters (can be an array)
81713 * @param mixed $default The default value to return if no result is found
81714 *
81715 * @return mixed The option value
81716 */
81717 public function getParameterOption($values, $default = false);
81718
81719 /**
81720 * Binds the current Input instance with the given arguments and options.
81721 *
81722 * @param InputDefinition $definition A InputDefinition instance
81723 */
81724 public function bind(InputDefinition $definition);
81725
81726 /**
81727 * Validates if arguments given are correct.
81728 *
81729 * Throws an exception when not enough arguments are given.
81730 *
81731 * @throws \RuntimeException
81732 */
81733 public function validate();
81734
81735 /**
81736 * Returns all the given arguments merged with the default values.
81737 *
81738 * @return array
81739 */
81740 public function getArguments();
81741
81742 /**
81743 * Gets argument by name.
81744 *
81745 * @param string $name The name of the argument
81746 *
81747 * @return mixed
81748 */
81749 public function getArgument($name);
81750
81751 /**
81752 * Sets an argument value by name.
81753 *
81754 * @param string $name The argument name
81755 * @param string $value The argument value
81756 *
81757 * @throws \InvalidArgumentException When argument given doesn't exist
81758 */
81759 public function setArgument($name, $value);
81760
81761 /**
81762 * Returns true if an InputArgument object exists by name or position.
81763 *
81764 * @param string|integer $name The InputArgument name or position
81765 *
81766 * @return Boolean true if the InputArgument object exists, false otherwise
81767 */
81768 public function hasArgument($name);
81769
81770 /**
81771 * Returns all the given options merged with the default values.
81772 *
81773 * @return array
81774 */
81775 public function getOptions();
81776
81777 /**
81778 * Gets an option by name.
81779 *
81780 * @param string $name The name of the option
81781 *
81782 * @return mixed
81783 */
81784 public function getOption($name);
81785
81786 /**
81787 * Sets an option value by name.
81788 *
81789 * @param string $name The option name
81790 * @param string $value The option value
81791 *
81792 * @throws \InvalidArgumentException When option given doesn't exist
81793 */
81794 public function setOption($name, $value);
81795
81796 /**
81797 * Returns true if an InputOption object exists by name.
81798 *
81799 * @param string $name The InputOption name
81800 *
81801 * @return Boolean true if the InputOption object exists, false otherwise
81802 */
81803 public function hasOption($name);
81804
81805 /**
81806 * Is this input means interactive?
81807 *
81808 * @return Boolean
81809 */
81810 public function isInteractive();
81811
81812 /**
81813 * Sets the input interactivity.
81814 *
81815 * @param Boolean $interactive If the input should be interactive
81816 */
81817 public function setInteractive($interactive);
81818 }
81819 <?php
81820
81821 /*
81822 * This file is part of the Symfony package.
81823 *
81824 * (c) Fabien Potencier <fabien@symfony.com>
81825 *
81826 * For the full copyright and license information, please view the LICENSE
81827 * file that was distributed with this source code.
81828 */
81829
81830 namespace Symfony\Component\Console\Input;
81831
81832 use Symfony\Component\Console\Descriptor\TextDescriptor;
81833 use Symfony\Component\Console\Descriptor\XmlDescriptor;
81834
81835 /**
81836 * A InputDefinition represents a set of valid command line arguments and options.
81837 *
81838 * Usage:
81839 *
81840 * $definition = new InputDefinition(array(
81841 * new InputArgument('name', InputArgument::REQUIRED),
81842 * new InputOption('foo', 'f', InputOption::VALUE_REQUIRED),
81843 * ));
81844 *
81845 * @author Fabien Potencier <fabien@symfony.com>
81846 *
81847 * @api
81848 */
81849 class InputDefinition
81850 {
81851 private $arguments;
81852 private $requiredCount;
81853 private $hasAnArrayArgument = false;
81854 private $hasOptional;
81855 private $options;
81856 private $shortcuts;
81857
81858 /**
81859 * Constructor.
81860 *
81861 * @param array $definition An array of InputArgument and InputOption instance
81862 *
81863 * @api
81864 */
81865 public function __construct(array $definition = array())
81866 {
81867 $this->setDefinition($definition);
81868 }
81869
81870 /**
81871 * Sets the definition of the input.
81872 *
81873 * @param array $definition The definition array
81874 *
81875 * @api
81876 */
81877 public function setDefinition(array $definition)
81878 {
81879 $arguments = array();
81880 $options = array();
81881 foreach ($definition as $item) {
81882 if ($item instanceof InputOption) {
81883 $options[] = $item;
81884 } else {
81885 $arguments[] = $item;
81886 }
81887 }
81888
81889 $this->setArguments($arguments);
81890 $this->setOptions($options);
81891 }
81892
81893 /**
81894 * Sets the InputArgument objects.
81895 *
81896 * @param InputArgument[] $arguments An array of InputArgument objects
81897 *
81898 * @api
81899 */
81900 public function setArguments($arguments = array())
81901 {
81902 $this->arguments = array();
81903 $this->requiredCount = 0;
81904 $this->hasOptional = false;
81905 $this->hasAnArrayArgument = false;
81906 $this->addArguments($arguments);
81907 }
81908
81909 /**
81910 * Adds an array of InputArgument objects.
81911 *
81912 * @param InputArgument[] $arguments An array of InputArgument objects
81913 *
81914 * @api
81915 */
81916 public function addArguments($arguments = array())
81917 {
81918 if (null !== $arguments) {
81919 foreach ($arguments as $argument) {
81920 $this->addArgument($argument);
81921 }
81922 }
81923 }
81924
81925 /**
81926 * Adds an InputArgument object.
81927 *
81928 * @param InputArgument $argument An InputArgument object
81929 *
81930 * @throws \LogicException When incorrect argument is given
81931 *
81932 * @api
81933 */
81934 public function addArgument(InputArgument $argument)
81935 {
81936 if (isset($this->arguments[$argument->getName()])) {
81937 throw new \LogicException(sprintf('An argument with name "%s" already exists.', $argument->getName()));
81938 }
81939
81940 if ($this->hasAnArrayArgument) {
81941 throw new \LogicException('Cannot add an argument after an array argument.');
81942 }
81943
81944 if ($argument->isRequired() && $this->hasOptional) {
81945 throw new \LogicException('Cannot add a required argument after an optional one.');
81946 }
81947
81948 if ($argument->isArray()) {
81949 $this->hasAnArrayArgument = true;
81950 }
81951
81952 if ($argument->isRequired()) {
81953 ++$this->requiredCount;
81954 } else {
81955 $this->hasOptional = true;
81956 }
81957
81958 $this->arguments[$argument->getName()] = $argument;
81959 }
81960
81961 /**
81962 * Returns an InputArgument by name or by position.
81963 *
81964 * @param string|integer $name The InputArgument name or position
81965 *
81966 * @return InputArgument An InputArgument object
81967 *
81968 * @throws \InvalidArgumentException When argument given doesn't exist
81969 *
81970 * @api
81971 */
81972 public function getArgument($name)
81973 {
81974 if (!$this->hasArgument($name)) {
81975 throw new \InvalidArgumentException(sprintf('The "%s" argument does not exist.', $name));
81976 }
81977
81978 $arguments = is_int($name) ? array_values($this->arguments) : $this->arguments;
81979
81980 return $arguments[$name];
81981 }
81982
81983 /**
81984 * Returns true if an InputArgument object exists by name or position.
81985 *
81986 * @param string|integer $name The InputArgument name or position
81987 *
81988 * @return Boolean true if the InputArgument object exists, false otherwise
81989 *
81990 * @api
81991 */
81992 public function hasArgument($name)
81993 {
81994 $arguments = is_int($name) ? array_values($this->arguments) : $this->arguments;
81995
81996 return isset($arguments[$name]);
81997 }
81998
81999 /**
82000 * Gets the array of InputArgument objects.
82001 *
82002 * @return InputArgument[] An array of InputArgument objects
82003 *
82004 * @api
82005 */
82006 public function getArguments()
82007 {
82008 return $this->arguments;
82009 }
82010
82011 /**
82012 * Returns the number of InputArguments.
82013 *
82014 * @return integer The number of InputArguments
82015 */
82016 public function getArgumentCount()
82017 {
82018 return $this->hasAnArrayArgument ? PHP_INT_MAX : count($this->arguments);
82019 }
82020
82021 /**
82022 * Returns the number of required InputArguments.
82023 *
82024 * @return integer The number of required InputArguments
82025 */
82026 public function getArgumentRequiredCount()
82027 {
82028 return $this->requiredCount;
82029 }
82030
82031 /**
82032 * Gets the default values.
82033 *
82034 * @return array An array of default values
82035 */
82036 public function getArgumentDefaults()
82037 {
82038 $values = array();
82039 foreach ($this->arguments as $argument) {
82040 $values[$argument->getName()] = $argument->getDefault();
82041 }
82042
82043 return $values;
82044 }
82045
82046 /**
82047 * Sets the InputOption objects.
82048 *
82049 * @param InputOption[] $options An array of InputOption objects
82050 *
82051 * @api
82052 */
82053 public function setOptions($options = array())
82054 {
82055 $this->options = array();
82056 $this->shortcuts = array();
82057 $this->addOptions($options);
82058 }
82059
82060 /**
82061 * Adds an array of InputOption objects.
82062 *
82063 * @param InputOption[] $options An array of InputOption objects
82064 *
82065 * @api
82066 */
82067 public function addOptions($options = array())
82068 {
82069 foreach ($options as $option) {
82070 $this->addOption($option);
82071 }
82072 }
82073
82074 /**
82075 * Adds an InputOption object.
82076 *
82077 * @param InputOption $option An InputOption object
82078 *
82079 * @throws \LogicException When option given already exist
82080 *
82081 * @api
82082 */
82083 public function addOption(InputOption $option)
82084 {
82085 if (isset($this->options[$option->getName()]) && !$option->equals($this->options[$option->getName()])) {
82086 throw new \LogicException(sprintf('An option named "%s" already exists.', $option->getName()));
82087 }
82088
82089 if ($option->getShortcut()) {
82090 foreach (explode('|', $option->getShortcut()) as $shortcut) {
82091 if (isset($this->shortcuts[$shortcut]) && !$option->equals($this->options[$this->shortcuts[$shortcut]])) {
82092 throw new \LogicException(sprintf('An option with shortcut "%s" already exists.', $shortcut));
82093 }
82094 }
82095 }
82096
82097 $this->options[$option->getName()] = $option;
82098 if ($option->getShortcut()) {
82099 foreach (explode('|', $option->getShortcut()) as $shortcut) {
82100 $this->shortcuts[$shortcut] = $option->getName();
82101 }
82102 }
82103 }
82104
82105 /**
82106 * Returns an InputOption by name.
82107 *
82108 * @param string $name The InputOption name
82109 *
82110 * @return InputOption A InputOption object
82111 *
82112 * @throws \InvalidArgumentException When option given doesn't exist
82113 *
82114 * @api
82115 */
82116 public function getOption($name)
82117 {
82118 if (!$this->hasOption($name)) {
82119 throw new \InvalidArgumentException(sprintf('The "--%s" option does not exist.', $name));
82120 }
82121
82122 return $this->options[$name];
82123 }
82124
82125 /**
82126 * Returns true if an InputOption object exists by name.
82127 *
82128 * @param string $name The InputOption name
82129 *
82130 * @return Boolean true if the InputOption object exists, false otherwise
82131 *
82132 * @api
82133 */
82134 public function hasOption($name)
82135 {
82136 return isset($this->options[$name]);
82137 }
82138
82139 /**
82140 * Gets the array of InputOption objects.
82141 *
82142 * @return InputOption[] An array of InputOption objects
82143 *
82144 * @api
82145 */
82146 public function getOptions()
82147 {
82148 return $this->options;
82149 }
82150
82151 /**
82152 * Returns true if an InputOption object exists by shortcut.
82153 *
82154 * @param string $name The InputOption shortcut
82155 *
82156 * @return Boolean true if the InputOption object exists, false otherwise
82157 */
82158 public function hasShortcut($name)
82159 {
82160 return isset($this->shortcuts[$name]);
82161 }
82162
82163 /**
82164 * Gets an InputOption by shortcut.
82165 *
82166 * @param string $shortcut the Shortcut name
82167 *
82168 * @return InputOption An InputOption object
82169 */
82170 public function getOptionForShortcut($shortcut)
82171 {
82172 return $this->getOption($this->shortcutToName($shortcut));
82173 }
82174
82175 /**
82176 * Gets an array of default values.
82177 *
82178 * @return array An array of all default values
82179 */
82180 public function getOptionDefaults()
82181 {
82182 $values = array();
82183 foreach ($this->options as $option) {
82184 $values[$option->getName()] = $option->getDefault();
82185 }
82186
82187 return $values;
82188 }
82189
82190 /**
82191 * Returns the InputOption name given a shortcut.
82192 *
82193 * @param string $shortcut The shortcut
82194 *
82195 * @return string The InputOption name
82196 *
82197 * @throws \InvalidArgumentException When option given does not exist
82198 */
82199 private function shortcutToName($shortcut)
82200 {
82201 if (!isset($this->shortcuts[$shortcut])) {
82202 throw new \InvalidArgumentException(sprintf('The "-%s" option does not exist.', $shortcut));
82203 }
82204
82205 return $this->shortcuts[$shortcut];
82206 }
82207
82208 /**
82209 * Gets the synopsis.
82210 *
82211 * @return string The synopsis
82212 */
82213 public function getSynopsis()
82214 {
82215 $elements = array();
82216 foreach ($this->getOptions() as $option) {
82217 $shortcut = $option->getShortcut() ? sprintf('-%s|', $option->getShortcut()) : '';
82218 $elements[] = sprintf('['.($option->isValueRequired() ? '%s--%s="..."' : ($option->isValueOptional() ? '%s--%s[="..."]' : '%s--%s')).']', $shortcut, $option->getName());
82219 }
82220
82221 foreach ($this->getArguments() as $argument) {
82222 $elements[] = sprintf($argument->isRequired() ? '%s' : '[%s]', $argument->getName().($argument->isArray() ? '1' : ''));
82223
82224 if ($argument->isArray()) {
82225 $elements[] = sprintf('... [%sN]', $argument->getName());
82226 }
82227 }
82228
82229 return implode(' ', $elements);
82230 }
82231
82232 /**
82233 * Returns a textual representation of the InputDefinition.
82234 *
82235 * @return string A string representing the InputDefinition
82236 *
82237 * @deprecated Deprecated since version 2.3, to be removed in 3.0.
82238 */
82239 public function asText()
82240 {
82241 $descriptor = new TextDescriptor();
82242
82243 return $descriptor->describe($this);
82244 }
82245
82246 /**
82247 * Returns an XML representation of the InputDefinition.
82248 *
82249 * @param Boolean $asDom Whether to return a DOM or an XML string
82250 *
82251 * @return string|\DOMDocument An XML string representing the InputDefinition
82252 *
82253 * @deprecated Deprecated since version 2.3, to be removed in 3.0.
82254 */
82255 public function asXml($asDom = false)
82256 {
82257 $descriptor = new XmlDescriptor();
82258
82259 return $descriptor->describe($this, array('as_dom' => $asDom));
82260 }
82261 }
82262 <?php
82263
82264 /*
82265 * This file is part of the Symfony package.
82266 *
82267 * (c) Fabien Potencier <fabien@symfony.com>
82268 *
82269 * For the full copyright and license information, please view the LICENSE
82270 * file that was distributed with this source code.
82271 */
82272
82273 namespace Symfony\Component\Console\Input;
82274
82275 /**
82276 * Represents a command line option.
82277 *
82278 * @author Fabien Potencier <fabien@symfony.com>
82279 *
82280 * @api
82281 */
82282 class InputOption
82283 {
82284 const VALUE_NONE = 1;
82285 const VALUE_REQUIRED = 2;
82286 const VALUE_OPTIONAL = 4;
82287 const VALUE_IS_ARRAY = 8;
82288
82289 private $name;
82290 private $shortcut;
82291 private $mode;
82292 private $default;
82293 private $description;
82294
82295 /**
82296 * Constructor.
82297 *
82298 * @param string $name The option name
82299 * @param string|array $shortcut The shortcuts, can be null, a string of shortcuts delimited by | or an array of shortcuts
82300 * @param integer $mode The option mode: One of the VALUE_* constants
82301 * @param string $description A description text
82302 * @param mixed $default The default value (must be null for self::VALUE_REQUIRED or self::VALUE_NONE)
82303 *
82304 * @throws \InvalidArgumentException If option mode is invalid or incompatible
82305 *
82306 * @api
82307 */
82308 public function __construct($name, $shortcut = null, $mode = null, $description = '', $default = null)
82309 {
82310 if (0 === strpos($name, '--')) {
82311 $name = substr($name, 2);
82312 }
82313
82314 if (empty($name)) {
82315 throw new \InvalidArgumentException('An option name cannot be empty.');
82316 }
82317
82318 if (empty($shortcut)) {
82319 $shortcut = null;
82320 }
82321
82322 if (null !== $shortcut) {
82323 if (is_array($shortcut)) {
82324 $shortcut = implode('|', $shortcut);
82325 }
82326 $shortcuts = preg_split('{(\|)-?}', ltrim($shortcut, '-'));
82327 $shortcuts = array_filter($shortcuts);
82328 $shortcut = implode('|', $shortcuts);
82329
82330 if (empty($shortcut)) {
82331 throw new \InvalidArgumentException('An option shortcut cannot be empty.');
82332 }
82333 }
82334
82335 if (null === $mode) {
82336 $mode = self::VALUE_NONE;
82337 } elseif (!is_int($mode) || $mode > 15 || $mode < 1) {
82338 throw new \InvalidArgumentException(sprintf('Option mode "%s" is not valid.', $mode));
82339 }
82340
82341 $this->name = $name;
82342 $this->shortcut = $shortcut;
82343 $this->mode = $mode;
82344 $this->description = $description;
82345
82346 if ($this->isArray() && !$this->acceptValue()) {
82347 throw new \InvalidArgumentException('Impossible to have an option mode VALUE_IS_ARRAY if the option does not accept a value.');
82348 }
82349
82350 $this->setDefault($default);
82351 }
82352
82353 /**
82354 * Returns the option shortcut.
82355 *
82356 * @return string The shortcut
82357 */
82358 public function getShortcut()
82359 {
82360 return $this->shortcut;
82361 }
82362
82363 /**
82364 * Returns the option name.
82365 *
82366 * @return string The name
82367 */
82368 public function getName()
82369 {
82370 return $this->name;
82371 }
82372
82373 /**
82374 * Returns true if the option accepts a value.
82375 *
82376 * @return Boolean true if value mode is not self::VALUE_NONE, false otherwise
82377 */
82378 public function acceptValue()
82379 {
82380 return $this->isValueRequired() || $this->isValueOptional();
82381 }
82382
82383 /**
82384 * Returns true if the option requires a value.
82385 *
82386 * @return Boolean true if value mode is self::VALUE_REQUIRED, false otherwise
82387 */
82388 public function isValueRequired()
82389 {
82390 return self::VALUE_REQUIRED === (self::VALUE_REQUIRED & $this->mode);
82391 }
82392
82393 /**
82394 * Returns true if the option takes an optional value.
82395 *
82396 * @return Boolean true if value mode is self::VALUE_OPTIONAL, false otherwise
82397 */
82398 public function isValueOptional()
82399 {
82400 return self::VALUE_OPTIONAL === (self::VALUE_OPTIONAL & $this->mode);
82401 }
82402
82403 /**
82404 * Returns true if the option can take multiple values.
82405 *
82406 * @return Boolean true if mode is self::VALUE_IS_ARRAY, false otherwise
82407 */
82408 public function isArray()
82409 {
82410 return self::VALUE_IS_ARRAY === (self::VALUE_IS_ARRAY & $this->mode);
82411 }
82412
82413 /**
82414 * Sets the default value.
82415 *
82416 * @param mixed $default The default value
82417 *
82418 * @throws \LogicException When incorrect default value is given
82419 */
82420 public function setDefault($default = null)
82421 {
82422 if (self::VALUE_NONE === (self::VALUE_NONE & $this->mode) && null !== $default) {
82423 throw new \LogicException('Cannot set a default value when using InputOption::VALUE_NONE mode.');
82424 }
82425
82426 if ($this->isArray()) {
82427 if (null === $default) {
82428 $default = array();
82429 } elseif (!is_array($default)) {
82430 throw new \LogicException('A default value for an array option must be an array.');
82431 }
82432 }
82433
82434 $this->default = $this->acceptValue() ? $default : false;
82435 }
82436
82437 /**
82438 * Returns the default value.
82439 *
82440 * @return mixed The default value
82441 */
82442 public function getDefault()
82443 {
82444 return $this->default;
82445 }
82446
82447 /**
82448 * Returns the description text.
82449 *
82450 * @return string The description text
82451 */
82452 public function getDescription()
82453 {
82454 return $this->description;
82455 }
82456
82457 /**
82458 * Checks whether the given option equals this one
82459 *
82460 * @param InputOption $option option to compare
82461 * @return Boolean
82462 */
82463 public function equals(InputOption $option)
82464 {
82465 return $option->getName() === $this->getName()
82466 && $option->getShortcut() === $this->getShortcut()
82467 && $option->getDefault() === $this->getDefault()
82468 && $option->isArray() === $this->isArray()
82469 && $option->isValueRequired() === $this->isValueRequired()
82470 && $option->isValueOptional() === $this->isValueOptional()
82471 ;
82472 }
82473 }
82474 <?php
82475
82476 /*
82477 * This file is part of the Symfony package.
82478 *
82479 * (c) Fabien Potencier <fabien@symfony.com>
82480 *
82481 * For the full copyright and license information, please view the LICENSE
82482 * file that was distributed with this source code.
82483 */
82484
82485 namespace Symfony\Component\Filesystem\Tests;
82486
82487 use Symfony\Component\Filesystem\Filesystem;
82488
82489 /**
82490 * Test class for Filesystem.
82491 */
82492 class FilesystemTest extends \PHPUnit_Framework_TestCase
82493 {
82494 /**
82495 * @var string $workspace
82496 */
82497 private $workspace = null;
82498
82499 /**
82500 * @var \Symfony\Component\Filesystem\Filesystem $filesystem
82501 */
82502 private $filesystem = null;
82503
82504 private static $symlinkOnWindows = null;
82505
82506 public static function setUpBeforeClass()
82507 {
82508 if (defined('PHP_WINDOWS_VERSION_MAJOR')) {
82509 self::$symlinkOnWindows = true;
82510 $originDir = tempnam(sys_get_temp_dir(), 'sl');
82511 $targetDir = tempnam(sys_get_temp_dir(), 'sl');
82512 if (true !== @symlink($originDir, $targetDir)) {
82513 $report = error_get_last();
82514 if (is_array($report) && false !== strpos($report['message'], 'error code(1314)')) {
82515 self::$symlinkOnWindows = false;
82516 }
82517 }
82518 }
82519 }
82520
82521 public function setUp()
82522 {
82523 $this->filesystem = new Filesystem();
82524 $this->workspace = rtrim(sys_get_temp_dir(), DIRECTORY_SEPARATOR).DIRECTORY_SEPARATOR.time().rand(0, 1000);
82525 mkdir($this->workspace, 0777, true);
82526 $this->workspace = realpath($this->workspace);
82527 }
82528
82529 public function tearDown()
82530 {
82531 $this->clean($this->workspace);
82532 }
82533
82534 /**
82535 * @param string $file
82536 */
82537 private function clean($file)
82538 {
82539 if (is_dir($file) && !is_link($file)) {
82540 $dir = new \FilesystemIterator($file);
82541 foreach ($dir as $childFile) {
82542 $this->clean($childFile);
82543 }
82544
82545 rmdir($file);
82546 } else {
82547 unlink($file);
82548 }
82549 }
82550
82551 public function testCopyCreatesNewFile()
82552 {
82553 $sourceFilePath = $this->workspace.DIRECTORY_SEPARATOR.'copy_source_file';
82554 $targetFilePath = $this->workspace.DIRECTORY_SEPARATOR.'copy_target_file';
82555
82556 file_put_contents($sourceFilePath, 'SOURCE FILE');
82557
82558 $this->filesystem->copy($sourceFilePath, $targetFilePath);
82559
82560 $this->assertFileExists($targetFilePath);
82561 $this->assertEquals('SOURCE FILE', file_get_contents($targetFilePath));
82562 }
82563
82564 /**
82565 * @expectedException \Symfony\Component\Filesystem\Exception\IOException
82566 */
82567 public function testCopyFails()
82568 {
82569 $sourceFilePath = $this->workspace.DIRECTORY_SEPARATOR.'copy_source_file';
82570 $targetFilePath = $this->workspace.DIRECTORY_SEPARATOR.'copy_target_file';
82571
82572 $this->filesystem->copy($sourceFilePath, $targetFilePath);
82573 }
82574
82575 public function testCopyOverridesExistingFileIfModified()
82576 {
82577 $sourceFilePath = $this->workspace.DIRECTORY_SEPARATOR.'copy_source_file';
82578 $targetFilePath = $this->workspace.DIRECTORY_SEPARATOR.'copy_target_file';
82579
82580 file_put_contents($sourceFilePath, 'SOURCE FILE');
82581 file_put_contents($targetFilePath, 'TARGET FILE');
82582 touch($targetFilePath, time() - 1000);
82583
82584 $this->filesystem->copy($sourceFilePath, $targetFilePath);
82585
82586 $this->assertFileExists($targetFilePath);
82587 $this->assertEquals('SOURCE FILE', file_get_contents($targetFilePath));
82588 }
82589
82590 public function testCopyDoesNotOverrideExistingFileByDefault()
82591 {
82592 $sourceFilePath = $this->workspace.DIRECTORY_SEPARATOR.'copy_source_file';
82593 $targetFilePath = $this->workspace.DIRECTORY_SEPARATOR.'copy_target_file';
82594
82595 file_put_contents($sourceFilePath, 'SOURCE FILE');
82596 file_put_contents($targetFilePath, 'TARGET FILE');
82597
82598 // make sure both files have the same modification time
82599 $modificationTime = time() - 1000;
82600 touch($sourceFilePath, $modificationTime);
82601 touch($targetFilePath, $modificationTime);
82602
82603 $this->filesystem->copy($sourceFilePath, $targetFilePath);
82604
82605 $this->assertFileExists($targetFilePath);
82606 $this->assertEquals('TARGET FILE', file_get_contents($targetFilePath));
82607 }
82608
82609 public function testCopyOverridesExistingFileIfForced()
82610 {
82611 $sourceFilePath = $this->workspace.DIRECTORY_SEPARATOR.'copy_source_file';
82612 $targetFilePath = $this->workspace.DIRECTORY_SEPARATOR.'copy_target_file';
82613
82614 file_put_contents($sourceFilePath, 'SOURCE FILE');
82615 file_put_contents($targetFilePath, 'TARGET FILE');
82616
82617 // make sure both files have the same modification time
82618 $modificationTime = time() - 1000;
82619 touch($sourceFilePath, $modificationTime);
82620 touch($targetFilePath, $modificationTime);
82621
82622 $this->filesystem->copy($sourceFilePath, $targetFilePath, true);
82623
82624 $this->assertFileExists($targetFilePath);
82625 $this->assertEquals('SOURCE FILE', file_get_contents($targetFilePath));
82626 }
82627
82628 public function testCopyCreatesTargetDirectoryIfItDoesNotExist()
82629 {
82630 $sourceFilePath = $this->workspace.DIRECTORY_SEPARATOR.'copy_source_file';
82631 $targetFileDirectory = $this->workspace.DIRECTORY_SEPARATOR.'directory';
82632 $targetFilePath = $targetFileDirectory.DIRECTORY_SEPARATOR.'copy_target_file';
82633
82634 file_put_contents($sourceFilePath, 'SOURCE FILE');
82635
82636 $this->filesystem->copy($sourceFilePath, $targetFilePath);
82637
82638 $this->assertTrue(is_dir($targetFileDirectory));
82639 $this->assertFileExists($targetFilePath);
82640 $this->assertEquals('SOURCE FILE', file_get_contents($targetFilePath));
82641 }
82642
82643 public function testMkdirCreatesDirectoriesRecursively()
82644 {
82645 $directory = $this->workspace
82646 .DIRECTORY_SEPARATOR.'directory'
82647 .DIRECTORY_SEPARATOR.'sub_directory';
82648
82649 $this->filesystem->mkdir($directory);
82650
82651 $this->assertTrue(is_dir($directory));
82652 }
82653
82654 public function testMkdirCreatesDirectoriesFromArray()
82655 {
82656 $basePath = $this->workspace.DIRECTORY_SEPARATOR;
82657 $directories = array(
82658 $basePath.'1', $basePath.'2', $basePath.'3'
82659 );
82660
82661 $this->filesystem->mkdir($directories);
82662
82663 $this->assertTrue(is_dir($basePath.'1'));
82664 $this->assertTrue(is_dir($basePath.'2'));
82665 $this->assertTrue(is_dir($basePath.'3'));
82666 }
82667
82668 public function testMkdirCreatesDirectoriesFromTraversableObject()
82669 {
82670 $basePath = $this->workspace.DIRECTORY_SEPARATOR;
82671 $directories = new \ArrayObject(array(
82672 $basePath.'1', $basePath.'2', $basePath.'3'
82673 ));
82674
82675 $this->filesystem->mkdir($directories);
82676
82677 $this->assertTrue(is_dir($basePath.'1'));
82678 $this->assertTrue(is_dir($basePath.'2'));
82679 $this->assertTrue(is_dir($basePath.'3'));
82680 }
82681
82682 /**
82683 * @expectedException \Symfony\Component\Filesystem\Exception\IOException
82684 */
82685 public function testMkdirCreatesDirectoriesFails()
82686 {
82687 $basePath = $this->workspace.DIRECTORY_SEPARATOR;
82688 $dir = $basePath.'2';
82689
82690 file_put_contents($dir, '');
82691
82692 $this->filesystem->mkdir($dir);
82693 }
82694
82695 public function testTouchCreatesEmptyFile()
82696 {
82697 $file = $this->workspace.DIRECTORY_SEPARATOR.'1';
82698
82699 $this->filesystem->touch($file);
82700
82701 $this->assertFileExists($file);
82702 }
82703
82704 /**
82705 * @expectedException \Symfony\Component\Filesystem\Exception\IOException
82706 */
82707 public function testTouchFails()
82708 {
82709 $file = $this->workspace.DIRECTORY_SEPARATOR.'1'.DIRECTORY_SEPARATOR.'2';
82710
82711 $this->filesystem->touch($file);
82712 }
82713
82714 public function testTouchCreatesEmptyFilesFromArray()
82715 {
82716 $basePath = $this->workspace.DIRECTORY_SEPARATOR;
82717 $files = array(
82718 $basePath.'1', $basePath.'2', $basePath.'3'
82719 );
82720
82721 $this->filesystem->touch($files);
82722
82723 $this->assertFileExists($basePath.'1');
82724 $this->assertFileExists($basePath.'2');
82725 $this->assertFileExists($basePath.'3');
82726 }
82727
82728 public function testTouchCreatesEmptyFilesFromTraversableObject()
82729 {
82730 $basePath = $this->workspace.DIRECTORY_SEPARATOR;
82731 $files = new \ArrayObject(array(
82732 $basePath.'1', $basePath.'2', $basePath.'3'
82733 ));
82734
82735 $this->filesystem->touch($files);
82736
82737 $this->assertFileExists($basePath.'1');
82738 $this->assertFileExists($basePath.'2');
82739 $this->assertFileExists($basePath.'3');
82740 }
82741
82742 public function testRemoveCleansFilesAndDirectoriesIteratively()
82743 {
82744 $basePath = $this->workspace.DIRECTORY_SEPARATOR.'directory'.DIRECTORY_SEPARATOR;
82745
82746 mkdir($basePath);
82747 mkdir($basePath.'dir');
82748 touch($basePath.'file');
82749
82750 $this->filesystem->remove($basePath);
82751
82752 $this->assertTrue(!is_dir($basePath));
82753 }
82754
82755 public function testRemoveCleansArrayOfFilesAndDirectories()
82756 {
82757 $basePath = $this->workspace.DIRECTORY_SEPARATOR;
82758
82759 mkdir($basePath.'dir');
82760 touch($basePath.'file');
82761
82762 $files = array(
82763 $basePath.'dir', $basePath.'file'
82764 );
82765
82766 $this->filesystem->remove($files);
82767
82768 $this->assertTrue(!is_dir($basePath.'dir'));
82769 $this->assertTrue(!is_file($basePath.'file'));
82770 }
82771
82772 public function testRemoveCleansTraversableObjectOfFilesAndDirectories()
82773 {
82774 $basePath = $this->workspace.DIRECTORY_SEPARATOR;
82775
82776 mkdir($basePath.'dir');
82777 touch($basePath.'file');
82778
82779 $files = new \ArrayObject(array(
82780 $basePath.'dir', $basePath.'file'
82781 ));
82782
82783 $this->filesystem->remove($files);
82784
82785 $this->assertTrue(!is_dir($basePath.'dir'));
82786 $this->assertTrue(!is_file($basePath.'file'));
82787 }
82788
82789 public function testRemoveIgnoresNonExistingFiles()
82790 {
82791 $basePath = $this->workspace.DIRECTORY_SEPARATOR;
82792
82793 mkdir($basePath.'dir');
82794
82795 $files = array(
82796 $basePath.'dir', $basePath.'file'
82797 );
82798
82799 $this->filesystem->remove($files);
82800
82801 $this->assertTrue(!is_dir($basePath.'dir'));
82802 }
82803
82804 public function testRemoveCleansInvalidLinks()
82805 {
82806 $this->markAsSkippedIfSymlinkIsMissing();
82807
82808 $basePath = $this->workspace.DIRECTORY_SEPARATOR.'directory'.DIRECTORY_SEPARATOR;
82809
82810 mkdir($basePath);
82811 mkdir($basePath.'dir');
82812 // create symlink to unexisting file
82813 @symlink($basePath.'file', $basePath.'link');
82814
82815 $this->filesystem->remove($basePath);
82816
82817 $this->assertTrue(!is_dir($basePath));
82818 }
82819
82820 public function testFilesExists()
82821 {
82822 $basePath = $this->workspace.DIRECTORY_SEPARATOR.'directory'.DIRECTORY_SEPARATOR;
82823
82824 mkdir($basePath);
82825 touch($basePath.'file1');
82826 mkdir($basePath.'folder');
82827
82828 $this->assertTrue($this->filesystem->exists($basePath.'file1'));
82829 $this->assertTrue($this->filesystem->exists($basePath.'folder'));
82830 }
82831
82832 public function testFilesExistsTraversableObjectOfFilesAndDirectories()
82833 {
82834 $basePath = $this->workspace.DIRECTORY_SEPARATOR;
82835
82836 mkdir($basePath.'dir');
82837 touch($basePath.'file');
82838
82839 $files = new \ArrayObject(array(
82840 $basePath.'dir', $basePath.'file'
82841 ));
82842
82843 $this->assertTrue($this->filesystem->exists($files));
82844 }
82845
82846 public function testFilesNotExistsTraversableObjectOfFilesAndDirectories()
82847 {
82848 $basePath = $this->workspace.DIRECTORY_SEPARATOR;
82849
82850 mkdir($basePath.'dir');
82851 touch($basePath.'file');
82852 touch($basePath.'file2');
82853
82854 $files = new \ArrayObject(array(
82855 $basePath.'dir', $basePath.'file', $basePath.'file2'
82856 ));
82857
82858 unlink($basePath.'file');
82859
82860 $this->assertFalse($this->filesystem->exists($files));
82861 }
82862
82863 public function testInvalidFileNotExists()
82864 {
82865 $basePath = $this->workspace.DIRECTORY_SEPARATOR.'directory'.DIRECTORY_SEPARATOR;
82866
82867 $this->assertFalse($this->filesystem->exists($basePath.time()));
82868 }
82869
82870 public function testChmodChangesFileMode()
82871 {
82872 $this->markAsSkippedIfChmodIsMissing();
82873
82874 $dir = $this->workspace.DIRECTORY_SEPARATOR.'dir';
82875 mkdir($dir);
82876 $file = $dir.DIRECTORY_SEPARATOR.'file';
82877 touch($file);
82878
82879 $this->filesystem->chmod($file, 0400);
82880 $this->filesystem->chmod($dir, 0753);
82881
82882 $this->assertEquals(753, $this->getFilePermissions($dir));
82883 $this->assertEquals(400, $this->getFilePermissions($file));
82884 }
82885
82886 public function testChmodWrongMod()
82887 {
82888 $this->markAsSkippedIfChmodIsMissing();
82889
82890 $dir = $this->workspace.DIRECTORY_SEPARATOR.'file';
82891 touch($dir);
82892
82893 $this->filesystem->chmod($dir, 'Wrongmode');
82894 }
82895
82896 public function testChmodRecursive()
82897 {
82898 $this->markAsSkippedIfChmodIsMissing();
82899
82900 $dir = $this->workspace.DIRECTORY_SEPARATOR.'dir';
82901 mkdir($dir);
82902 $file = $dir.DIRECTORY_SEPARATOR.'file';
82903 touch($file);
82904
82905 $this->filesystem->chmod($file, 0400, 0000, true);
82906 $this->filesystem->chmod($dir, 0753, 0000, true);
82907
82908 $this->assertEquals(753, $this->getFilePermissions($dir));
82909 $this->assertEquals(753, $this->getFilePermissions($file));
82910 }
82911
82912 public function testChmodAppliesUmask()
82913 {
82914 $this->markAsSkippedIfChmodIsMissing();
82915
82916 $file = $this->workspace.DIRECTORY_SEPARATOR.'file';
82917 touch($file);
82918
82919 $this->filesystem->chmod($file, 0770, 0022);
82920 $this->assertEquals(750, $this->getFilePermissions($file));
82921 }
82922
82923 public function testChmodChangesModeOfArrayOfFiles()
82924 {
82925 $this->markAsSkippedIfChmodIsMissing();
82926
82927 $directory = $this->workspace.DIRECTORY_SEPARATOR.'directory';
82928 $file = $this->workspace.DIRECTORY_SEPARATOR.'file';
82929 $files = array($directory, $file);
82930
82931 mkdir($directory);
82932 touch($file);
82933
82934 $this->filesystem->chmod($files, 0753);
82935
82936 $this->assertEquals(753, $this->getFilePermissions($file));
82937 $this->assertEquals(753, $this->getFilePermissions($directory));
82938 }
82939
82940 public function testChmodChangesModeOfTraversableFileObject()
82941 {
82942 $this->markAsSkippedIfChmodIsMissing();
82943
82944 $directory = $this->workspace.DIRECTORY_SEPARATOR.'directory';
82945 $file = $this->workspace.DIRECTORY_SEPARATOR.'file';
82946 $files = new \ArrayObject(array($directory, $file));
82947
82948 mkdir($directory);
82949 touch($file);
82950
82951 $this->filesystem->chmod($files, 0753);
82952
82953 $this->assertEquals(753, $this->getFilePermissions($file));
82954 $this->assertEquals(753, $this->getFilePermissions($directory));
82955 }
82956
82957 public function testChown()
82958 {
82959 $this->markAsSkippedIfPosixIsMissing();
82960
82961 $dir = $this->workspace.DIRECTORY_SEPARATOR.'dir';
82962 mkdir($dir);
82963
82964 $this->filesystem->chown($dir, $this->getFileOwner($dir));
82965 }
82966
82967 public function testChownRecursive()
82968 {
82969 $this->markAsSkippedIfPosixIsMissing();
82970
82971 $dir = $this->workspace.DIRECTORY_SEPARATOR.'dir';
82972 mkdir($dir);
82973 $file = $dir.DIRECTORY_SEPARATOR.'file';
82974 touch($file);
82975
82976 $this->filesystem->chown($dir, $this->getFileOwner($dir), true);
82977 }
82978
82979 public function testChownSymlink()
82980 {
82981 $this->markAsSkippedIfSymlinkIsMissing();
82982
82983 $file = $this->workspace.DIRECTORY_SEPARATOR.'file';
82984 $link = $this->workspace.DIRECTORY_SEPARATOR.'link';
82985
82986 touch($file);
82987
82988 $this->filesystem->symlink($file, $link);
82989
82990 $this->filesystem->chown($link, $this->getFileOwner($link));
82991 }
82992
82993 /**
82994 * @expectedException \Symfony\Component\Filesystem\Exception\IOException
82995 */
82996 public function testChownSymlinkFails()
82997 {
82998 $this->markAsSkippedIfSymlinkIsMissing();
82999
83000 $file = $this->workspace.DIRECTORY_SEPARATOR.'file';
83001 $link = $this->workspace.DIRECTORY_SEPARATOR.'link';
83002
83003 touch($file);
83004
83005 $this->filesystem->symlink($file, $link);
83006
83007 $this->filesystem->chown($link, 'user'.time().mt_rand(1000, 9999));
83008 }
83009
83010 /**
83011 * @expectedException \Symfony\Component\Filesystem\Exception\IOException
83012 */
83013 public function testChownFail()
83014 {
83015 $this->markAsSkippedIfPosixIsMissing();
83016
83017 $dir = $this->workspace.DIRECTORY_SEPARATOR.'dir';
83018 mkdir($dir);
83019
83020 $this->filesystem->chown($dir, 'user'.time().mt_rand(1000, 9999));
83021 }
83022
83023 public function testChgrp()
83024 {
83025 $this->markAsSkippedIfPosixIsMissing();
83026
83027 $dir = $this->workspace.DIRECTORY_SEPARATOR.'dir';
83028 mkdir($dir);
83029
83030 $this->filesystem->chgrp($dir, $this->getFileGroup($dir));
83031 }
83032
83033 public function testChgrpRecursive()
83034 {
83035 $this->markAsSkippedIfPosixIsMissing();
83036
83037 $dir = $this->workspace.DIRECTORY_SEPARATOR.'dir';
83038 mkdir($dir);
83039 $file = $dir.DIRECTORY_SEPARATOR.'file';
83040 touch($file);
83041
83042 $this->filesystem->chgrp($dir, $this->getFileGroup($dir), true);
83043 }
83044
83045 public function testChgrpSymlink()
83046 {
83047 $this->markAsSkippedIfSymlinkIsMissing();
83048
83049 $file = $this->workspace.DIRECTORY_SEPARATOR.'file';
83050 $link = $this->workspace.DIRECTORY_SEPARATOR.'link';
83051
83052 touch($file);
83053
83054 $this->filesystem->symlink($file, $link);
83055
83056 $this->filesystem->chgrp($link, $this->getFileGroup($link));
83057 }
83058
83059 /**
83060 * @expectedException \Symfony\Component\Filesystem\Exception\IOException
83061 */
83062 public function testChgrpSymlinkFails()
83063 {
83064 $this->markAsSkippedIfSymlinkIsMissing();
83065
83066 $file = $this->workspace.DIRECTORY_SEPARATOR.'file';
83067 $link = $this->workspace.DIRECTORY_SEPARATOR.'link';
83068
83069 touch($file);
83070
83071 $this->filesystem->symlink($file, $link);
83072
83073 $this->filesystem->chgrp($link, 'user'.time().mt_rand(1000, 9999));
83074 }
83075
83076 /**
83077 * @expectedException \Symfony\Component\Filesystem\Exception\IOException
83078 */
83079 public function testChgrpFail()
83080 {
83081 $this->markAsSkippedIfPosixIsMissing();
83082
83083 $dir = $this->workspace.DIRECTORY_SEPARATOR.'dir';
83084 mkdir($dir);
83085
83086 $this->filesystem->chgrp($dir, 'user'.time().mt_rand(1000, 9999));
83087 }
83088
83089 public function testRename()
83090 {
83091 $file = $this->workspace.DIRECTORY_SEPARATOR.'file';
83092 $newPath = $this->workspace.DIRECTORY_SEPARATOR.'new_file';
83093 touch($file);
83094
83095 $this->filesystem->rename($file, $newPath);
83096
83097 $this->assertFileNotExists($file);
83098 $this->assertFileExists($newPath);
83099 }
83100
83101 /**
83102 * @expectedException \Symfony\Component\Filesystem\Exception\IOException
83103 */
83104 public function testRenameThrowsExceptionIfTargetAlreadyExists()
83105 {
83106 $file = $this->workspace.DIRECTORY_SEPARATOR.'file';
83107 $newPath = $this->workspace.DIRECTORY_SEPARATOR.'new_file';
83108
83109 touch($file);
83110 touch($newPath);
83111
83112 $this->filesystem->rename($file, $newPath);
83113 }
83114
83115 public function testRenameOverwritesTheTargetIfItAlreadyExists()
83116 {
83117 $file = $this->workspace.DIRECTORY_SEPARATOR.'file';
83118 $newPath = $this->workspace.DIRECTORY_SEPARATOR.'new_file';
83119
83120 touch($file);
83121 touch($newPath);
83122
83123 $this->filesystem->rename($file, $newPath, true);
83124
83125 $this->assertFileNotExists($file);
83126 $this->assertFileExists($newPath);
83127 }
83128
83129 /**
83130 * @expectedException \Symfony\Component\Filesystem\Exception\IOException
83131 */
83132 public function testRenameThrowsExceptionOnError()
83133 {
83134 $file = $this->workspace.DIRECTORY_SEPARATOR.uniqid();
83135 $newPath = $this->workspace.DIRECTORY_SEPARATOR.'new_file';
83136
83137 $this->filesystem->rename($file, $newPath);
83138 }
83139
83140 public function testSymlink()
83141 {
83142 $this->markAsSkippedIfSymlinkIsMissing();
83143
83144 $file = $this->workspace.DIRECTORY_SEPARATOR.'file';
83145 $link = $this->workspace.DIRECTORY_SEPARATOR.'link';
83146
83147 touch($file);
83148
83149 $this->filesystem->symlink($file, $link);
83150
83151 $this->assertTrue(is_link($link));
83152 $this->assertEquals($file, readlink($link));
83153 }
83154
83155 /**
83156 * @depends testSymlink
83157 */
83158 public function testRemoveSymlink()
83159 {
83160 $this->markAsSkippedIfSymlinkIsMissing();
83161
83162 $link = $this->workspace.DIRECTORY_SEPARATOR.'link';
83163
83164 $this->filesystem->remove($link);
83165
83166 $this->assertTrue(!is_link($link));
83167 }
83168
83169 public function testSymlinkIsOverwrittenIfPointsToDifferentTarget()
83170 {
83171 $this->markAsSkippedIfSymlinkIsMissing();
83172
83173 $file = $this->workspace.DIRECTORY_SEPARATOR.'file';
83174 $link = $this->workspace.DIRECTORY_SEPARATOR.'link';
83175
83176 touch($file);
83177 symlink($this->workspace, $link);
83178
83179 $this->filesystem->symlink($file, $link);
83180
83181 $this->assertTrue(is_link($link));
83182 $this->assertEquals($file, readlink($link));
83183 }
83184
83185 public function testSymlinkIsNotOverwrittenIfAlreadyCreated()
83186 {
83187 $this->markAsSkippedIfSymlinkIsMissing();
83188
83189 $file = $this->workspace.DIRECTORY_SEPARATOR.'file';
83190 $link = $this->workspace.DIRECTORY_SEPARATOR.'link';
83191
83192 touch($file);
83193 symlink($file, $link);
83194
83195 $this->filesystem->symlink($file, $link);
83196
83197 $this->assertTrue(is_link($link));
83198 $this->assertEquals($file, readlink($link));
83199 }
83200
83201 public function testSymlinkCreatesTargetDirectoryIfItDoesNotExist()
83202 {
83203 $this->markAsSkippedIfSymlinkIsMissing();
83204
83205 $file = $this->workspace.DIRECTORY_SEPARATOR.'file';
83206 $link1 = $this->workspace.DIRECTORY_SEPARATOR.'dir'.DIRECTORY_SEPARATOR.'link';
83207 $link2 = $this->workspace.DIRECTORY_SEPARATOR.'dir'.DIRECTORY_SEPARATOR.'subdir'.DIRECTORY_SEPARATOR.'link';
83208
83209 touch($file);
83210
83211 $this->filesystem->symlink($file, $link1);
83212 $this->filesystem->symlink($file, $link2);
83213
83214 $this->assertTrue(is_link($link1));
83215 $this->assertEquals($file, readlink($link1));
83216 $this->assertTrue(is_link($link2));
83217 $this->assertEquals($file, readlink($link2));
83218 }
83219
83220 /**
83221 * @dataProvider providePathsForMakePathRelative
83222 */
83223 public function testMakePathRelative($endPath, $startPath, $expectedPath)
83224 {
83225 $path = $this->filesystem->makePathRelative($endPath, $startPath);
83226
83227 $this->assertEquals($expectedPath, $path);
83228 }
83229
83230 /**
83231 * @return array
83232 */
83233 public function providePathsForMakePathRelative()
83234 {
83235 $paths = array(
83236 array('/var/lib/symfony/src/Symfony/', '/var/lib/symfony/src/Symfony/Component', '../'),
83237 array('/var/lib/symfony/src/Symfony/', '/var/lib/symfony/src/Symfony/Component/', '../'),
83238 array('/var/lib/symfony/src/Symfony', '/var/lib/symfony/src/Symfony/Component', '../'),
83239 array('/var/lib/symfony/src/Symfony', '/var/lib/symfony/src/Symfony/Component/', '../'),
83240 array('var/lib/symfony/', 'var/lib/symfony/src/Symfony/Component', '../../../'),
83241 array('/usr/lib/symfony/', '/var/lib/symfony/src/Symfony/Component', '../../../../../../usr/lib/symfony/'),
83242 array('/var/lib/symfony/src/Symfony/', '/var/lib/symfony/', 'src/Symfony/'),
83243 array('/aa/bb', '/aa/bb', './'),
83244 array('/aa/bb', '/aa/bb/', './'),
83245 array('/aa/bb/', '/aa/bb', './'),
83246 array('/aa/bb/', '/aa/bb/', './'),
83247 array('/aa/bb/cc', '/aa/bb/cc/dd', '../'),
83248 array('/aa/bb/cc', '/aa/bb/cc/dd/', '../'),
83249 array('/aa/bb/cc/', '/aa/bb/cc/dd', '../'),
83250 array('/aa/bb/cc/', '/aa/bb/cc/dd/', '../'),
83251 array('/aa/bb/cc', '/aa', 'bb/cc/'),
83252 array('/aa/bb/cc', '/aa/', 'bb/cc/'),
83253 array('/aa/bb/cc/', '/aa', 'bb/cc/'),
83254 array('/aa/bb/cc/', '/aa/', 'bb/cc/'),
83255 array('/a/aab/bb', '/a/aa', '../aab/bb/'),
83256 array('/a/aab/bb', '/a/aa/', '../aab/bb/'),
83257 array('/a/aab/bb/', '/a/aa', '../aab/bb/'),
83258 array('/a/aab/bb/', '/a/aa/', '../aab/bb/'),
83259 );
83260
83261 if (defined('PHP_WINDOWS_VERSION_MAJOR')) {
83262 $paths[] = array('c:\var\lib/symfony/src/Symfony/', 'c:/var/lib/symfony/', 'src/Symfony/');
83263 }
83264
83265 return $paths;
83266 }
83267
83268 public function testMirrorCopiesFilesAndDirectoriesRecursively()
83269 {
83270 $sourcePath = $this->workspace.DIRECTORY_SEPARATOR.'source'.DIRECTORY_SEPARATOR;
83271 $directory = $sourcePath.'directory'.DIRECTORY_SEPARATOR;
83272 $file1 = $directory.'file1';
83273 $file2 = $sourcePath.'file2';
83274
83275 mkdir($sourcePath);
83276 mkdir($directory);
83277 file_put_contents($file1, 'FILE1');
83278 file_put_contents($file2, 'FILE2');
83279
83280 $targetPath = $this->workspace.DIRECTORY_SEPARATOR.'target'.DIRECTORY_SEPARATOR;
83281
83282 $this->filesystem->mirror($sourcePath, $targetPath);
83283
83284 $this->assertTrue(is_dir($targetPath));
83285 $this->assertTrue(is_dir($targetPath.'directory'));
83286 $this->assertFileEquals($file1, $targetPath.'directory'.DIRECTORY_SEPARATOR.'file1');
83287 $this->assertFileEquals($file2, $targetPath.'file2');
83288
83289 $this->filesystem->remove($file1);
83290
83291 $this->filesystem->mirror($sourcePath, $targetPath, null, array('delete' => false));
83292 $this->assertTrue($this->filesystem->exists($targetPath.'directory'.DIRECTORY_SEPARATOR.'file1'));
83293
83294 $this->filesystem->mirror($sourcePath, $targetPath, null, array('delete' => true));
83295 $this->assertFalse($this->filesystem->exists($targetPath.'directory'.DIRECTORY_SEPARATOR.'file1'));
83296
83297 file_put_contents($file1, 'FILE1');
83298
83299 $this->filesystem->mirror($sourcePath, $targetPath, null, array('delete' => true));
83300 $this->assertTrue($this->filesystem->exists($targetPath.'directory'.DIRECTORY_SEPARATOR.'file1'));
83301
83302 $this->filesystem->remove($directory);
83303 $this->filesystem->mirror($sourcePath, $targetPath, null, array('delete' => true));
83304 $this->assertFalse($this->filesystem->exists($targetPath.'directory'));
83305 $this->assertFalse($this->filesystem->exists($targetPath.'directory'.DIRECTORY_SEPARATOR.'file1'));
83306 }
83307
83308 public function testMirrorCopiesLinks()
83309 {
83310 $this->markAsSkippedIfSymlinkIsMissing();
83311
83312 $sourcePath = $this->workspace.DIRECTORY_SEPARATOR.'source'.DIRECTORY_SEPARATOR;
83313
83314 mkdir($sourcePath);
83315 file_put_contents($sourcePath.'file1', 'FILE1');
83316 symlink($sourcePath.'file1', $sourcePath.'link1');
83317
83318 $targetPath = $this->workspace.DIRECTORY_SEPARATOR.'target'.DIRECTORY_SEPARATOR;
83319
83320 $this->filesystem->mirror($sourcePath, $targetPath);
83321
83322 $this->assertTrue(is_dir($targetPath));
83323 $this->assertFileEquals($sourcePath.'file1', $targetPath.DIRECTORY_SEPARATOR.'link1');
83324 $this->assertTrue(is_link($targetPath.DIRECTORY_SEPARATOR.'link1'));
83325 }
83326
83327 public function testMirrorCopiesLinkedDirectoryContents()
83328 {
83329 $this->markAsSkippedIfSymlinkIsMissing();
83330
83331 $sourcePath = $this->workspace.DIRECTORY_SEPARATOR.'source'.DIRECTORY_SEPARATOR;
83332
83333 mkdir($sourcePath.'nested/', 0777, true);
83334 file_put_contents($sourcePath.'/nested/file1.txt', 'FILE1');
83335 // Note: We symlink directory, not file
83336 symlink($sourcePath.'nested', $sourcePath.'link1');
83337
83338 $targetPath = $this->workspace.DIRECTORY_SEPARATOR.'target'.DIRECTORY_SEPARATOR;
83339
83340 $this->filesystem->mirror($sourcePath, $targetPath);
83341
83342 $this->assertTrue(is_dir($targetPath));
83343 $this->assertFileEquals($sourcePath.'/nested/file1.txt', $targetPath.DIRECTORY_SEPARATOR.'link1/file1.txt');
83344 $this->assertTrue(is_link($targetPath.DIRECTORY_SEPARATOR.'link1'));
83345 }
83346
83347 /**
83348 * @dataProvider providePathsForIsAbsolutePath
83349 */
83350 public function testIsAbsolutePath($path, $expectedResult)
83351 {
83352 $result = $this->filesystem->isAbsolutePath($path);
83353
83354 $this->assertEquals($expectedResult, $result);
83355 }
83356
83357 /**
83358 * @return array
83359 */
83360 public function providePathsForIsAbsolutePath()
83361 {
83362 return array(
83363 array('/var/lib', true),
83364 array('c:\\\\var\\lib', true),
83365 array('\\var\\lib', true),
83366 array('var/lib', false),
83367 array('../var/lib', false),
83368 array('', false),
83369 array(null, false)
83370 );
83371 }
83372
83373 public function testDumpFile()
83374 {
83375 $filename = $this->workspace.DIRECTORY_SEPARATOR.'foo'.DIRECTORY_SEPARATOR.'baz.txt';
83376
83377 $this->filesystem->dumpFile($filename, 'bar', 0753);
83378
83379 $this->assertFileExists($filename);
83380 $this->assertSame('bar', file_get_contents($filename));
83381
83382 // skip mode check on windows
83383 if (!defined('PHP_WINDOWS_VERSION_MAJOR')) {
83384 $this->assertEquals(753, $this->getFilePermissions($filename));
83385 }
83386 }
83387
83388 public function testDumpFileOverwritesAnExistingFile()
83389 {
83390 $filename = $this->workspace.DIRECTORY_SEPARATOR.'foo.txt';
83391 file_put_contents($filename, 'FOO BAR');
83392
83393 $this->filesystem->dumpFile($filename, 'bar');
83394
83395 $this->assertFileExists($filename);
83396 $this->assertSame('bar', file_get_contents($filename));
83397 }
83398
83399 /**
83400 * Returns file permissions as three digits (i.e. 755)
83401 *
83402 * @param string $filePath
83403 *
83404 * @return integer
83405 */
83406 private function getFilePermissions($filePath)
83407 {
83408 return (int) substr(sprintf('%o', fileperms($filePath)), -3);
83409 }
83410
83411 private function getFileOwner($filepath)
83412 {
83413 $this->markAsSkippedIfPosixIsMissing();
83414
83415 $infos = stat($filepath);
83416 if ($datas = posix_getpwuid($infos['uid'])) {
83417 return $datas['name'];
83418 }
83419 }
83420
83421 private function getFileGroup($filepath)
83422 {
83423 $this->markAsSkippedIfPosixIsMissing();
83424
83425 $infos = stat($filepath);
83426 if ($datas = posix_getgrgid($infos['gid'])) {
83427 return $datas['name'];
83428 }
83429 }
83430
83431 private function markAsSkippedIfSymlinkIsMissing()
83432 {
83433 if (!function_exists('symlink')) {
83434 $this->markTestSkipped('symlink is not supported');
83435 }
83436
83437 if (defined('PHP_WINDOWS_VERSION_MAJOR') && false === self::$symlinkOnWindows) {
83438 $this->markTestSkipped('symlink requires "Create symbolic links" privilege on windows');
83439 }
83440 }
83441
83442 private function markAsSkippedIfChmodIsMissing()
83443 {
83444 if (defined('PHP_WINDOWS_VERSION_MAJOR')) {
83445 $this->markTestSkipped('chmod is not supported on windows');
83446 }
83447 }
83448
83449 private function markAsSkippedIfPosixIsMissing()
83450 {
83451 if (defined('PHP_WINDOWS_VERSION_MAJOR') || !function_exists('posix_isatty')) {
83452 $this->markTestSkipped('Posix is not supported');
83453 }
83454 }
83455 }
83456 Filesystem Component
83457 ====================
83458
83459 Filesystem provides basic utility to manipulate the file system:
83460
83461 ```php
83462 <?php
83463
83464 use Symfony\Component\Filesystem\Filesystem;
83465
83466 $filesystem = new Filesystem();
83467
83468 $filesystem->copy($originFile, $targetFile, $override = false);
83469
83470 $filesystem->mkdir($dirs, $mode = 0777);
83471
83472 $filesystem->touch($files, $time = null, $atime = null);
83473
83474 $filesystem->remove($files);
83475
83476 $filesystem->chmod($files, $mode, $umask = 0000, $recursive = false);
83477
83478 $filesystem->chown($files, $user, $recursive = false);
83479
83480 $filesystem->chgrp($files, $group, $recursive = false);
83481
83482 $filesystem->rename($origin, $target);
83483
83484 $filesystem->symlink($originDir, $targetDir, $copyOnWindows = false);
83485
83486 $filesystem->makePathRelative($endPath, $startPath);
83487
83488 $filesystem->mirror($originDir, $targetDir, \Traversable $iterator = null, $options = array());
83489
83490 $filesystem->isAbsolutePath($file);
83491 ```
83492
83493 Resources
83494 ---------
83495
83496 You can run the unit tests with the following command:
83497
83498 $ cd path/to/Symfony/Component/Filesystem/
83499 $ composer.phar install --dev
83500 $ phpunit
83501 Copyright (c) 2004-2013 Fabien Potencier
83502
83503 Permission is hereby granted, free of charge, to any person obtaining a copy
83504 of this software and associated documentation files (the "Software"), to deal
83505 in the Software without restriction, including without limitation the rights
83506 to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
83507 copies of the Software, and to permit persons to whom the Software is furnished
83508 to do so, subject to the following conditions:
83509
83510 The above copyright notice and this permission notice shall be included in all
83511 copies or substantial portions of the Software.
83512
83513 THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
83514 IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
83515 FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
83516 AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
83517 LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
83518 OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
83519 THE SOFTWARE.
83520 <?php
83521
83522 /*
83523 * This file is part of the Symfony package.
83524 *
83525 * (c) Fabien Potencier <fabien@symfony.com>
83526 *
83527 * For the full copyright and license information, please view the LICENSE
83528 * file that was distributed with this source code.
83529 */
83530
83531 namespace Symfony\Component\Filesystem;
83532
83533 use Symfony\Component\Filesystem\Exception\IOException;
83534
83535 /**
83536 * Provides basic utility to manipulate the file system.
83537 *
83538 * @author Fabien Potencier <fabien@symfony.com>
83539 */
83540 class Filesystem
83541 {
83542 /**
83543 * Copies a file.
83544 *
83545 * This method only copies the file if the origin file is newer than the target file.
83546 *
83547 * By default, if the target already exists, it is not overridden.
83548 *
83549 * @param string $originFile The original filename
83550 * @param string $targetFile The target filename
83551 * @param boolean $override Whether to override an existing file or not
83552 *
83553 * @throws IOException When copy fails
83554 */
83555 public function copy($originFile, $targetFile, $override = false)
83556 {
83557 if (stream_is_local($originFile) && !is_file($originFile)) {
83558 throw new IOException(sprintf('Failed to copy %s because file not exists', $originFile));
83559 }
83560
83561 $this->mkdir(dirname($targetFile));
83562
83563 if (!$override && is_file($targetFile)) {
83564 $doCopy = filemtime($originFile) > filemtime($targetFile);
83565 } else {
83566 $doCopy = true;
83567 }
83568
83569 if ($doCopy) {
83570 // https://bugs.php.net/bug.php?id=64634
83571 $source = fopen($originFile, 'r');
83572 $target = fopen($targetFile, 'w+');
83573 stream_copy_to_stream($source, $target);
83574 fclose($source);
83575 fclose($target);
83576 unset($source, $target);
83577
83578 if (!is_file($targetFile)) {
83579 throw new IOException(sprintf('Failed to copy %s to %s', $originFile, $targetFile));
83580 }
83581 }
83582 }
83583
83584 /**
83585 * Creates a directory recursively.
83586 *
83587 * @param string|array|\Traversable $dirs The directory path
83588 * @param integer $mode The directory mode
83589 *
83590 * @throws IOException On any directory creation failure
83591 */
83592 public function mkdir($dirs, $mode = 0777)
83593 {
83594 foreach ($this->toIterator($dirs) as $dir) {
83595 if (is_dir($dir)) {
83596 continue;
83597 }
83598
83599 if (true !== @mkdir($dir, $mode, true)) {
83600 throw new IOException(sprintf('Failed to create %s', $dir));
83601 }
83602 }
83603 }
83604
83605 /**
83606 * Checks the existence of files or directories.
83607 *
83608 * @param string|array|\Traversable $files A filename, an array of files, or a \Traversable instance to check
83609 *
83610 * @return Boolean true if the file exists, false otherwise
83611 */
83612 public function exists($files)
83613 {
83614 foreach ($this->toIterator($files) as $file) {
83615 if (!file_exists($file)) {
83616 return false;
83617 }
83618 }
83619
83620 return true;
83621 }
83622
83623 /**
83624 * Sets access and modification time of file.
83625 *
83626 * @param string|array|\Traversable $files A filename, an array of files, or a \Traversable instance to create
83627 * @param integer $time The touch time as a unix timestamp
83628 * @param integer $atime The access time as a unix timestamp
83629 *
83630 * @throws IOException When touch fails
83631 */
83632 public function touch($files, $time = null, $atime = null)
83633 {
83634 foreach ($this->toIterator($files) as $file) {
83635 $touch = $time ? @touch($file, $time, $atime) : @touch($file);
83636 if (true !== $touch) {
83637 throw new IOException(sprintf('Failed to touch %s', $file));
83638 }
83639 }
83640 }
83641
83642 /**
83643 * Removes files or directories.
83644 *
83645 * @param string|array|\Traversable $files A filename, an array of files, or a \Traversable instance to remove
83646 *
83647 * @throws IOException When removal fails
83648 */
83649 public function remove($files)
83650 {
83651 $files = iterator_to_array($this->toIterator($files));
83652 $files = array_reverse($files);
83653 foreach ($files as $file) {
83654 if (!file_exists($file) && !is_link($file)) {
83655 continue;
83656 }
83657
83658 if (is_dir($file) && !is_link($file)) {
83659 $this->remove(new \FilesystemIterator($file));
83660
83661 if (true !== @rmdir($file)) {
83662 throw new IOException(sprintf('Failed to remove directory %s', $file));
83663 }
83664 } else {
83665 // https://bugs.php.net/bug.php?id=52176
83666 if (defined('PHP_WINDOWS_VERSION_MAJOR') && is_dir($file)) {
83667 if (true !== @rmdir($file)) {
83668 throw new IOException(sprintf('Failed to remove file %s', $file));
83669 }
83670 } else {
83671 if (true !== @unlink($file)) {
83672 throw new IOException(sprintf('Failed to remove file %s', $file));
83673 }
83674 }
83675 }
83676 }
83677 }
83678
83679 /**
83680 * Change mode for an array of files or directories.
83681 *
83682 * @param string|array|\Traversable $files A filename, an array of files, or a \Traversable instance to change mode
83683 * @param integer $mode The new mode (octal)
83684 * @param integer $umask The mode mask (octal)
83685 * @param Boolean $recursive Whether change the mod recursively or not
83686 *
83687 * @throws IOException When the change fail
83688 */
83689 public function chmod($files, $mode, $umask = 0000, $recursive = false)
83690 {
83691 foreach ($this->toIterator($files) as $file) {
83692 if ($recursive && is_dir($file) && !is_link($file)) {
83693 $this->chmod(new \FilesystemIterator($file), $mode, $umask, true);
83694 }
83695 if (true !== @chmod($file, $mode & ~$umask)) {
83696 throw new IOException(sprintf('Failed to chmod file %s', $file));
83697 }
83698 }
83699 }
83700
83701 /**
83702 * Change the owner of an array of files or directories
83703 *
83704 * @param string|array|\Traversable $files A filename, an array of files, or a \Traversable instance to change owner
83705 * @param string $user The new owner user name
83706 * @param Boolean $recursive Whether change the owner recursively or not
83707 *
83708 * @throws IOException When the change fail
83709 */
83710 public function chown($files, $user, $recursive = false)
83711 {
83712 foreach ($this->toIterator($files) as $file) {
83713 if ($recursive && is_dir($file) && !is_link($file)) {
83714 $this->chown(new \FilesystemIterator($file), $user, true);
83715 }
83716 if (is_link($file) && function_exists('lchown')) {
83717 if (true !== @lchown($file, $user)) {
83718 throw new IOException(sprintf('Failed to chown file %s', $file));
83719 }
83720 } else {
83721 if (true !== @chown($file, $user)) {
83722 throw new IOException(sprintf('Failed to chown file %s', $file));
83723 }
83724 }
83725 }
83726 }
83727
83728 /**
83729 * Change the group of an array of files or directories
83730 *
83731 * @param string|array|\Traversable $files A filename, an array of files, or a \Traversable instance to change group
83732 * @param string $group The group name
83733 * @param Boolean $recursive Whether change the group recursively or not
83734 *
83735 * @throws IOException When the change fail
83736 */
83737 public function chgrp($files, $group, $recursive = false)
83738 {
83739 foreach ($this->toIterator($files) as $file) {
83740 if ($recursive && is_dir($file) && !is_link($file)) {
83741 $this->chgrp(new \FilesystemIterator($file), $group, true);
83742 }
83743 if (is_link($file) && function_exists('lchgrp')) {
83744 if (true !== @lchgrp($file, $group)) {
83745 throw new IOException(sprintf('Failed to chgrp file %s', $file));
83746 }
83747 } else {
83748 if (true !== @chgrp($file, $group)) {
83749 throw new IOException(sprintf('Failed to chgrp file %s', $file));
83750 }
83751 }
83752 }
83753 }
83754
83755 /**
83756 * Renames a file or a directory.
83757 *
83758 * @param string $origin The origin filename or directory
83759 * @param string $target The new filename or directory
83760 * @param Boolean $overwrite Whether to overwrite the target if it already exists
83761 *
83762 * @throws IOException When target file or directory already exists
83763 * @throws IOException When origin cannot be renamed
83764 */
83765 public function rename($origin, $target, $overwrite = false)
83766 {
83767 // we check that target does not exist
83768 if (!$overwrite && is_readable($target)) {
83769 throw new IOException(sprintf('Cannot rename because the target "%s" already exist.', $target));
83770 }
83771
83772 if (true !== @rename($origin, $target)) {
83773 throw new IOException(sprintf('Cannot rename "%s" to "%s".', $origin, $target));
83774 }
83775 }
83776
83777 /**
83778 * Creates a symbolic link or copy a directory.
83779 *
83780 * @param string $originDir The origin directory path
83781 * @param string $targetDir The symbolic link name
83782 * @param Boolean $copyOnWindows Whether to copy files if on Windows
83783 *
83784 * @throws IOException When symlink fails
83785 */
83786 public function symlink($originDir, $targetDir, $copyOnWindows = false)
83787 {
83788 if (!function_exists('symlink') && $copyOnWindows) {
83789 $this->mirror($originDir, $targetDir);
83790
83791 return;
83792 }
83793
83794 $this->mkdir(dirname($targetDir));
83795
83796 $ok = false;
83797 if (is_link($targetDir)) {
83798 if (readlink($targetDir) != $originDir) {
83799 $this->remove($targetDir);
83800 } else {
83801 $ok = true;
83802 }
83803 }
83804
83805 if (!$ok) {
83806 if (true !== @symlink($originDir, $targetDir)) {
83807 $report = error_get_last();
83808 if (is_array($report)) {
83809 if (defined('PHP_WINDOWS_VERSION_MAJOR') && false !== strpos($report['message'], 'error code(1314)')) {
83810 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?');
83811 }
83812 }
83813 throw new IOException(sprintf('Failed to create symbolic link from %s to %s', $originDir, $targetDir));
83814 }
83815 }
83816 }
83817
83818 /**
83819 * Given an existing path, convert it to a path relative to a given starting path
83820 *
83821 * @param string $endPath Absolute path of target
83822 * @param string $startPath Absolute path where traversal begins
83823 *
83824 * @return string Path of target relative to starting path
83825 */
83826 public function makePathRelative($endPath, $startPath)
83827 {
83828 // Normalize separators on windows
83829 if (defined('PHP_WINDOWS_VERSION_MAJOR')) {
83830 $endPath = strtr($endPath, '\\', '/');
83831 $startPath = strtr($startPath, '\\', '/');
83832 }
83833
83834 // Split the paths into arrays
83835 $startPathArr = explode('/', trim($startPath, '/'));
83836 $endPathArr = explode('/', trim($endPath, '/'));
83837
83838 // Find for which directory the common path stops
83839 $index = 0;
83840 while (isset($startPathArr[$index]) && isset($endPathArr[$index]) && $startPathArr[$index] === $endPathArr[$index]) {
83841 $index++;
83842 }
83843
83844 // Determine how deep the start path is relative to the common path (ie, "web/bundles" = 2 levels)
83845 $depth = count($startPathArr) - $index;
83846
83847 // Repeated "../" for each level need to reach the common path
83848 $traverser = str_repeat('../', $depth);
83849
83850 $endPathRemainder = implode('/', array_slice($endPathArr, $index));
83851
83852 // Construct $endPath from traversing to the common path, then to the remaining $endPath
83853 $relativePath = $traverser.(strlen($endPathRemainder) > 0 ? $endPathRemainder.'/' : '');
83854
83855 return (strlen($relativePath) === 0) ? './' : $relativePath;
83856 }
83857
83858 /**
83859 * Mirrors a directory to another.
83860 *
83861 * @param string $originDir The origin directory
83862 * @param string $targetDir The target directory
83863 * @param \Traversable $iterator A Traversable instance
83864 * @param array $options An array of boolean options
83865 * Valid options are:
83866 * - $options['override'] Whether to override an existing file on copy or not (see copy())
83867 * - $options['copy_on_windows'] Whether to copy files instead of links on Windows (see symlink())
83868 * - $options['delete'] Whether to delete files that are not in the source directory (defaults to false)
83869 *
83870 * @throws IOException When file type is unknown
83871 */
83872 public function mirror($originDir, $targetDir, \Traversable $iterator = null, $options = array())
83873 {
83874 $targetDir = rtrim($targetDir, '/\\');
83875 $originDir = rtrim($originDir, '/\\');
83876
83877 // Iterate in destination folder to remove obsolete entries
83878 if ($this->exists($targetDir) && isset($options['delete']) && $options['delete']) {
83879 $deleteIterator = $iterator;
83880 if (null === $deleteIterator) {
83881 $flags = \FilesystemIterator::SKIP_DOTS;
83882 $deleteIterator = new \RecursiveIteratorIterator(new \RecursiveDirectoryIterator($targetDir, $flags), \RecursiveIteratorIterator::CHILD_FIRST);
83883 }
83884 foreach ($deleteIterator as $file) {
83885 $origin = str_replace($targetDir, $originDir, $file->getPathname());
83886 if (!$this->exists($origin)) {
83887 $this->remove($file);
83888 }
83889 }
83890 }
83891
83892 $copyOnWindows = false;
83893 if (isset($options['copy_on_windows']) && !function_exists('symlink')) {
83894 $copyOnWindows = $options['copy_on_windows'];
83895 }
83896
83897 if (null === $iterator) {
83898 $flags = $copyOnWindows ? \FilesystemIterator::SKIP_DOTS | \FilesystemIterator::FOLLOW_SYMLINKS : \FilesystemIterator::SKIP_DOTS;
83899 $iterator = new \RecursiveIteratorIterator(new \RecursiveDirectoryIterator($originDir, $flags), \RecursiveIteratorIterator::SELF_FIRST);
83900 }
83901
83902 foreach ($iterator as $file) {
83903 $target = str_replace($originDir, $targetDir, $file->getPathname());
83904
83905 if ($copyOnWindows) {
83906 if (is_link($file) || is_file($file)) {
83907 $this->copy($file, $target, isset($options['override']) ? $options['override'] : false);
83908 } elseif (is_dir($file)) {
83909 $this->mkdir($target);
83910 } else {
83911 throw new IOException(sprintf('Unable to guess "%s" file type.', $file));
83912 }
83913 } else {
83914 if (is_link($file)) {
83915 $this->symlink($file, $target);
83916 } elseif (is_dir($file)) {
83917 $this->mkdir($target);
83918 } elseif (is_file($file)) {
83919 $this->copy($file, $target, isset($options['override']) ? $options['override'] : false);
83920 } else {
83921 throw new IOException(sprintf('Unable to guess "%s" file type.', $file));
83922 }
83923 }
83924 }
83925 }
83926
83927 /**
83928 * Returns whether the file path is an absolute path.
83929 *
83930 * @param string $file A file path
83931 *
83932 * @return Boolean
83933 */
83934 public function isAbsolutePath($file)
83935 {
83936 if (strspn($file, '/\\', 0, 1)
83937 || (strlen($file) > 3 && ctype_alpha($file[0])
83938 && substr($file, 1, 1) === ':'
83939 && (strspn($file, '/\\', 2, 1))
83940 )
83941 || null !== parse_url($file, PHP_URL_SCHEME)
83942 ) {
83943 return true;
83944 }
83945
83946 return false;
83947 }
83948
83949 /**
83950 * @param mixed $files
83951 *
83952 * @return \Traversable
83953 */
83954 private function toIterator($files)
83955 {
83956 if (!$files instanceof \Traversable) {
83957 $files = new \ArrayObject(is_array($files) ? $files : array($files));
83958 }
83959
83960 return $files;
83961 }
83962
83963 /**
83964 * Atomically dumps content into a file.
83965 *
83966 * @param string $filename The file to be written to.
83967 * @param string $content The data to write into the file.
83968 * @param integer $mode The file mode (octal).
83969 * @throws IOException If the file cannot be written to.
83970 */
83971 public function dumpFile($filename, $content, $mode = 0666)
83972 {
83973 $dir = dirname($filename);
83974
83975 if (!is_dir($dir)) {
83976 $this->mkdir($dir);
83977 } elseif (!is_writable($dir)) {
83978 throw new IOException(sprintf('Unable to write in the %s directory\n', $dir));
83979 }
83980
83981 $tmpFile = tempnam($dir, basename($filename));
83982
83983 if (false === @file_put_contents($tmpFile, $content)) {
83984 throw new IOException(sprintf('Failed to write file "%s".', $filename));
83985 }
83986
83987 $this->rename($tmpFile, $filename, true);
83988 $this->chmod($filename, $mode);
83989 }
83990 }
83991 <?php
83992
83993 /*
83994 * This file is part of the Symfony package.
83995 *
83996 * (c) Fabien Potencier <fabien@symfony.com>
83997 *
83998 * For the full copyright and license information, please view the LICENSE
83999 * file that was distributed with this source code.
84000 */
84001
84002 namespace Symfony\Component\Filesystem\Exception;
84003
84004 /**
84005 * Exception class thrown when a filesystem operation failure happens
84006 *
84007 * @author Romain Neutron <imprec@gmail.com>
84008 *
84009 * @api
84010 */
84011 class IOException extends \RuntimeException implements ExceptionInterface
84012 {
84013
84014 }
84015 <?php
84016
84017 /*
84018 * This file is part of the Symfony package.
84019 *
84020 * (c) Fabien Potencier <fabien@symfony.com>
84021 *
84022 * For the full copyright and license information, please view the LICENSE
84023 * file that was distributed with this source code.
84024 */
84025
84026 namespace Symfony\Component\Filesystem\Exception;
84027
84028 /**
84029 * Exception interface for all exceptions thrown by the component.
84030 *
84031 * @author Romain Neutron <imprec@gmail.com>
84032 *
84033 * @api
84034 */
84035 interface ExceptionInterface
84036 {
84037
84038 }
84039 CHANGELOG
84040 =========
84041
84042 2.3.0
84043 -----
84044
84045 * added the dumpFile() method to atomically write files
84046
84047 2.2.0
84048 -----
84049
84050 * added a delete option for the mirror() method
84051
84052 2.1.0
84053 -----
84054
84055 * 24eb396 : BC Break : mkdir() function now throws exception in case of failure instead of returning Boolean value
84056 * created the component
84057 {
84058 "name": "symfony/filesystem",
84059 "type": "library",
84060 "description": "Symfony Filesystem Component",
84061 "keywords": [],
84062 "homepage": "http://symfony.com",
84063 "license": "MIT",
84064 "authors": [
84065 {
84066 "name": "Fabien Potencier",
84067 "email": "fabien@symfony.com"
84068 },
84069 {
84070 "name": "Symfony Community",
84071 "homepage": "http://symfony.com/contributors"
84072 }
84073 ],
84074 "require": {
84075 "php": ">=5.3.3"
84076 },
84077 "autoload": {
84078 "psr-0": { "Symfony\\Component\\Filesystem\\": "" }
84079 },
84080 "target-dir": "Symfony/Component/Filesystem",
84081 "minimum-stability": "dev",
84082 "extra": {
84083 "branch-alias": {
84084 "dev-master": "2.3-dev"
84085 }
84086 }
84087 }
84088 <?xml version="1.0" encoding="UTF-8"?>
84089
84090 <phpunit backupGlobals="false"
84091 backupStaticAttributes="false"
84092 colors="true"
84093 convertErrorsToExceptions="true"
84094 convertNoticesToExceptions="true"
84095 convertWarningsToExceptions="true"
84096 processIsolation="false"
84097 stopOnFailure="false"
84098 syntaxCheck="false"
84099 bootstrap="vendor/autoload.php"
84100 >
84101 <testsuites>
84102 <testsuite name="Symfony Filesystem Component Test Suite">
84103 <directory>./Tests/</directory>
84104 </testsuite>
84105 </testsuites>
84106
84107 <filter>
84108 <whitelist>
84109 <directory>./</directory>
84110 <exclude>
84111 <directory>./Tests</directory>
84112 </exclude>
84113 </whitelist>
84114 </filter>
84115 </phpunit>
84116 <?php
84117
84118 /*
84119 * This file is part of the Symfony package.
84120 *
84121 * (c) Fabien Potencier <fabien@symfony.com>
84122 *
84123 * For the full copyright and license information, please view the LICENSE
84124 * file that was distributed with this source code.
84125 */
84126
84127 namespace Symfony\Component\EventDispatcher\Tests;
84128
84129 use Symfony\Component\EventDispatcher\Event;
84130 use Symfony\Component\EventDispatcher\EventDispatcher;
84131
84132 /**
84133 * Test class for Event.
84134 */
84135 class EventTest extends \PHPUnit_Framework_TestCase
84136 {
84137 /**
84138 * @var \Symfony\Component\EventDispatcher\Event
84139 */
84140 protected $event;
84141
84142 /**
84143 * @var \Symfony\Component\EventDispatcher\EventDispatcher
84144 */
84145 protected $dispatcher;
84146
84147 /**
84148 * Sets up the fixture, for example, opens a network connection.
84149 * This method is called before a test is executed.
84150 */
84151 protected function setUp()
84152 {
84153 $this->event = new Event;
84154 $this->dispatcher = new EventDispatcher();
84155 }
84156
84157 /**
84158 * Tears down the fixture, for example, closes a network connection.
84159 * This method is called after a test is executed.
84160 */
84161 protected function tearDown()
84162 {
84163 $this->event = null;
84164 $this->eventDispatcher = null;
84165 }
84166
84167 public function testIsPropagationStopped()
84168 {
84169 $this->assertFalse($this->event->isPropagationStopped());
84170 }
84171
84172 public function testStopPropagationAndIsPropagationStopped()
84173 {
84174 $this->event->stopPropagation();
84175 $this->assertTrue($this->event->isPropagationStopped());
84176 }
84177
84178 public function testSetDispatcher()
84179 {
84180 $this->event->setDispatcher($this->dispatcher);
84181 $this->assertSame($this->dispatcher, $this->event->getDispatcher());
84182 }
84183
84184 public function testGetDispatcher()
84185 {
84186 $this->assertNull($this->event->getDispatcher());
84187 }
84188
84189 public function testGetName()
84190 {
84191 $this->assertNull($this->event->getName());
84192 }
84193
84194 public function testSetName()
84195 {
84196 $this->event->setName('foo');
84197 $this->assertEquals('foo', $this->event->getName());
84198 }
84199 }
84200 <?php
84201
84202 /*
84203 * This file is part of the Symfony package.
84204 *
84205 * (c) Fabien Potencier <fabien@symfony.com>
84206 *
84207 * For the full copyright and license information, please view the LICENSE
84208 * file that was distributed with this source code.
84209 */
84210
84211 namespace Symfony\Component\EventDispatcher\Tests;
84212
84213 use Symfony\Component\EventDispatcher\GenericEvent;
84214
84215 /**
84216 * Test class for Event.
84217 */
84218 class GenericEventTest extends \PHPUnit_Framework_TestCase
84219 {
84220
84221 /**
84222 * @var GenericEvent
84223 */
84224 private $event;
84225
84226 private $subject;
84227
84228 /**
84229 * Prepares the environment before running a test.
84230 */
84231 protected function setUp()
84232 {
84233 parent::setUp();
84234
84235 $this->subject = new \StdClass();
84236 $this->event = new GenericEvent($this->subject, array('name' => 'Event'), 'foo');
84237 }
84238
84239 /**
84240 * Cleans up the environment after running a test.
84241 */
84242 protected function tearDown()
84243 {
84244 $this->subject = null;
84245 $this->event = null;
84246
84247 parent::tearDown();
84248 }
84249
84250 public function testConstruct()
84251 {
84252 $this->assertEquals($this->event, new GenericEvent($this->subject, array('name' => 'Event')));
84253 }
84254
84255 /**
84256 * Tests Event->getArgs()
84257 */
84258 public function testGetArguments()
84259 {
84260 // test getting all
84261 $this->assertSame(array('name' => 'Event'), $this->event->getArguments());
84262 }
84263
84264 public function testSetArguments()
84265 {
84266 $result = $this->event->setArguments(array('foo' => 'bar'));
84267 $this->assertAttributeSame(array('foo' => 'bar'), 'arguments', $this->event);
84268 $this->assertSame($this->event, $result);
84269 }
84270
84271 public function testSetArgument()
84272 {
84273 $result = $this->event->setArgument('foo2', 'bar2');
84274 $this->assertAttributeSame(array('name' => 'Event', 'foo2' => 'bar2'), 'arguments', $this->event);
84275 $this->assertEquals($this->event, $result);
84276 }
84277
84278 public function testGetArgument()
84279 {
84280 // test getting key
84281 $this->assertEquals('Event', $this->event->getArgument('name'));
84282 }
84283
84284 /**
84285 * @expectedException \InvalidArgumentException
84286 */
84287 public function testGetArgException()
84288 {
84289 $this->event->getArgument('nameNotExist');
84290 }
84291
84292 public function testOffsetGet()
84293 {
84294 // test getting key
84295 $this->assertEquals('Event', $this->event['name']);
84296
84297 // test getting invalid arg
84298 $this->setExpectedException('InvalidArgumentException');
84299 $this->assertFalse($this->event['nameNotExist']);
84300 }
84301
84302 public function testOffsetSet()
84303 {
84304 $this->event['foo2'] = 'bar2';
84305 $this->assertAttributeSame(array('name' => 'Event', 'foo2' => 'bar2'), 'arguments', $this->event);
84306 }
84307
84308 public function testOffsetUnset()
84309 {
84310 unset($this->event['name']);
84311 $this->assertAttributeSame(array(), 'arguments', $this->event);
84312 }
84313
84314 public function testOffsetIsset()
84315 {
84316 $this->assertTrue(isset($this->event['name']));
84317 $this->assertFalse(isset($this->event['nameNotExist']));
84318 }
84319
84320 public function testHasArgument()
84321 {
84322 $this->assertTrue($this->event->hasArgument('name'));
84323 $this->assertFalse($this->event->hasArgument('nameNotExist'));
84324 }
84325
84326 public function testGetSubject()
84327 {
84328 $this->assertSame($this->subject, $this->event->getSubject());
84329 }
84330
84331 public function testHasIterator()
84332 {
84333 $data = array();
84334 foreach ($this->event as $key => $value) {
84335 $data[$key] = $value;
84336 }
84337 $this->assertEquals(array('name' => 'Event'), $data);
84338 }
84339 }
84340 <?php
84341
84342 /*
84343 * This file is part of the Symfony package.
84344 *
84345 * (c) Fabien Potencier <fabien@symfony.com>
84346 *
84347 * For the full copyright and license information, please view the LICENSE
84348 * file that was distributed with this source code.
84349 */
84350
84351 namespace Symfony\Component\EventDispatcher\Tests;
84352
84353 use Symfony\Component\EventDispatcher\Event;
84354 use Symfony\Component\EventDispatcher\EventDispatcher;
84355 use Symfony\Component\EventDispatcher\EventSubscriberInterface;
84356
84357 class EventDispatcherTest extends \PHPUnit_Framework_TestCase
84358 {
84359 /* Some pseudo events */
84360 const preFoo = 'pre.foo';
84361 const postFoo = 'post.foo';
84362 const preBar = 'pre.bar';
84363 const postBar = 'post.bar';
84364
84365 private $dispatcher;
84366
84367 private $listener;
84368
84369 protected function setUp()
84370 {
84371 $this->dispatcher = new EventDispatcher();
84372 $this->listener = new TestEventListener();
84373 }
84374
84375 protected function tearDown()
84376 {
84377 $this->dispatcher = null;
84378 $this->listener = null;
84379 }
84380
84381 public function testInitialState()
84382 {
84383 $this->assertEquals(array(), $this->dispatcher->getListeners());
84384 $this->assertFalse($this->dispatcher->hasListeners(self::preFoo));
84385 $this->assertFalse($this->dispatcher->hasListeners(self::postFoo));
84386 }
84387
84388 public function testAddListener()
84389 {
84390 $this->dispatcher->addListener('pre.foo', array($this->listener, 'preFoo'));
84391 $this->dispatcher->addListener('post.foo', array($this->listener, 'postFoo'));
84392 $this->assertTrue($this->dispatcher->hasListeners(self::preFoo));
84393 $this->assertTrue($this->dispatcher->hasListeners(self::postFoo));
84394 $this->assertCount(1, $this->dispatcher->getListeners(self::preFoo));
84395 $this->assertCount(1, $this->dispatcher->getListeners(self::postFoo));
84396 $this->assertCount(2, $this->dispatcher->getListeners());
84397 }
84398
84399 public function testGetListenersSortsByPriority()
84400 {
84401 $listener1 = new TestEventListener();
84402 $listener2 = new TestEventListener();
84403 $listener3 = new TestEventListener();
84404 $listener1->name = '1';
84405 $listener2->name = '2';
84406 $listener3->name = '3';
84407
84408 $this->dispatcher->addListener('pre.foo', array($listener1, 'preFoo'), -10);
84409 $this->dispatcher->addListener('pre.foo', array($listener2, 'preFoo'), 10);
84410 $this->dispatcher->addListener('pre.foo', array($listener3, 'preFoo'));
84411
84412 $expected = array(
84413 array($listener2, 'preFoo'),
84414 array($listener3, 'preFoo'),
84415 array($listener1, 'preFoo'),
84416 );
84417
84418 $this->assertSame($expected, $this->dispatcher->getListeners('pre.foo'));
84419 }
84420
84421 public function testGetAllListenersSortsByPriority()
84422 {
84423 $listener1 = new TestEventListener();
84424 $listener2 = new TestEventListener();
84425 $listener3 = new TestEventListener();
84426 $listener4 = new TestEventListener();
84427 $listener5 = new TestEventListener();
84428 $listener6 = new TestEventListener();
84429
84430 $this->dispatcher->addListener('pre.foo', $listener1, -10);
84431 $this->dispatcher->addListener('pre.foo', $listener2);
84432 $this->dispatcher->addListener('pre.foo', $listener3, 10);
84433 $this->dispatcher->addListener('post.foo', $listener4, -10);
84434 $this->dispatcher->addListener('post.foo', $listener5);
84435 $this->dispatcher->addListener('post.foo', $listener6, 10);
84436
84437 $expected = array(
84438 'pre.foo' => array($listener3, $listener2, $listener1),
84439 'post.foo' => array($listener6, $listener5, $listener4),
84440 );
84441
84442 $this->assertSame($expected, $this->dispatcher->getListeners());
84443 }
84444
84445 public function testDispatch()
84446 {
84447 $this->dispatcher->addListener('pre.foo', array($this->listener, 'preFoo'));
84448 $this->dispatcher->addListener('post.foo', array($this->listener, 'postFoo'));
84449 $this->dispatcher->dispatch(self::preFoo);
84450 $this->assertTrue($this->listener->preFooInvoked);
84451 $this->assertFalse($this->listener->postFooInvoked);
84452 $this->assertInstanceOf('Symfony\Component\EventDispatcher\Event', $this->dispatcher->dispatch('noevent'));
84453 $this->assertInstanceOf('Symfony\Component\EventDispatcher\Event', $this->dispatcher->dispatch(self::preFoo));
84454 $event = new Event();
84455 $return = $this->dispatcher->dispatch(self::preFoo, $event);
84456 $this->assertEquals('pre.foo', $event->getName());
84457 $this->assertSame($event, $return);
84458 }
84459
84460 public function testDispatchForClosure()
84461 {
84462 $invoked = 0;
84463 $listener = function () use (&$invoked) {
84464 $invoked++;
84465 };
84466 $this->dispatcher->addListener('pre.foo', $listener);
84467 $this->dispatcher->addListener('post.foo', $listener);
84468 $this->dispatcher->dispatch(self::preFoo);
84469 $this->assertEquals(1, $invoked);
84470 }
84471
84472 public function testStopEventPropagation()
84473 {
84474 $otherListener = new TestEventListener();
84475
84476 // postFoo() stops the propagation, so only one listener should
84477 // be executed
84478 // Manually set priority to enforce $this->listener to be called first
84479 $this->dispatcher->addListener('post.foo', array($this->listener, 'postFoo'), 10);
84480 $this->dispatcher->addListener('post.foo', array($otherListener, 'preFoo'));
84481 $this->dispatcher->dispatch(self::postFoo);
84482 $this->assertTrue($this->listener->postFooInvoked);
84483 $this->assertFalse($otherListener->postFooInvoked);
84484 }
84485
84486 public function testDispatchByPriority()
84487 {
84488 $invoked = array();
84489 $listener1 = function () use (&$invoked) {
84490 $invoked[] = '1';
84491 };
84492 $listener2 = function () use (&$invoked) {
84493 $invoked[] = '2';
84494 };
84495 $listener3 = function () use (&$invoked) {
84496 $invoked[] = '3';
84497 };
84498 $this->dispatcher->addListener('pre.foo', $listener1, -10);
84499 $this->dispatcher->addListener('pre.foo', $listener2);
84500 $this->dispatcher->addListener('pre.foo', $listener3, 10);
84501 $this->dispatcher->dispatch(self::preFoo);
84502 $this->assertEquals(array('3', '2', '1'), $invoked);
84503 }
84504
84505 public function testRemoveListener()
84506 {
84507 $this->dispatcher->addListener('pre.bar', $this->listener);
84508 $this->assertTrue($this->dispatcher->hasListeners(self::preBar));
84509 $this->dispatcher->removeListener('pre.bar', $this->listener);
84510 $this->assertFalse($this->dispatcher->hasListeners(self::preBar));
84511 $this->dispatcher->removeListener('notExists', $this->listener);
84512 }
84513
84514 public function testAddSubscriber()
84515 {
84516 $eventSubscriber = new TestEventSubscriber();
84517 $this->dispatcher->addSubscriber($eventSubscriber);
84518 $this->assertTrue($this->dispatcher->hasListeners(self::preFoo));
84519 $this->assertTrue($this->dispatcher->hasListeners(self::postFoo));
84520 }
84521
84522 public function testAddSubscriberWithPriorities()
84523 {
84524 $eventSubscriber = new TestEventSubscriber();
84525 $this->dispatcher->addSubscriber($eventSubscriber);
84526
84527 $eventSubscriber = new TestEventSubscriberWithPriorities();
84528 $this->dispatcher->addSubscriber($eventSubscriber);
84529
84530 $listeners = $this->dispatcher->getListeners('pre.foo');
84531 $this->assertTrue($this->dispatcher->hasListeners(self::preFoo));
84532 $this->assertCount(2, $listeners);
84533 $this->assertInstanceOf('Symfony\Component\EventDispatcher\Tests\TestEventSubscriberWithPriorities', $listeners[0][0]);
84534 }
84535
84536 public function testAddSubscriberWithMultipleListeners()
84537 {
84538 $eventSubscriber = new TestEventSubscriberWithMultipleListeners();
84539 $this->dispatcher->addSubscriber($eventSubscriber);
84540
84541 $listeners = $this->dispatcher->getListeners('pre.foo');
84542 $this->assertTrue($this->dispatcher->hasListeners(self::preFoo));
84543 $this->assertCount(2, $listeners);
84544 $this->assertEquals('preFoo2', $listeners[0][1]);
84545 }
84546
84547 public function testRemoveSubscriber()
84548 {
84549 $eventSubscriber = new TestEventSubscriber();
84550 $this->dispatcher->addSubscriber($eventSubscriber);
84551 $this->assertTrue($this->dispatcher->hasListeners(self::preFoo));
84552 $this->assertTrue($this->dispatcher->hasListeners(self::postFoo));
84553 $this->dispatcher->removeSubscriber($eventSubscriber);
84554 $this->assertFalse($this->dispatcher->hasListeners(self::preFoo));
84555 $this->assertFalse($this->dispatcher->hasListeners(self::postFoo));
84556 }
84557
84558 public function testRemoveSubscriberWithPriorities()
84559 {
84560 $eventSubscriber = new TestEventSubscriberWithPriorities();
84561 $this->dispatcher->addSubscriber($eventSubscriber);
84562 $this->assertTrue($this->dispatcher->hasListeners(self::preFoo));
84563 $this->dispatcher->removeSubscriber($eventSubscriber);
84564 $this->assertFalse($this->dispatcher->hasListeners(self::preFoo));
84565 }
84566
84567 public function testRemoveSubscriberWithMultipleListeners()
84568 {
84569 $eventSubscriber = new TestEventSubscriberWithMultipleListeners();
84570 $this->dispatcher->addSubscriber($eventSubscriber);
84571 $this->assertTrue($this->dispatcher->hasListeners(self::preFoo));
84572 $this->assertCount(2, $this->dispatcher->getListeners(self::preFoo));
84573 $this->dispatcher->removeSubscriber($eventSubscriber);
84574 $this->assertFalse($this->dispatcher->hasListeners(self::preFoo));
84575 }
84576
84577 public function testEventReceivesTheDispatcherInstance()
84578 {
84579 $test = $this;
84580 $this->dispatcher->addListener('test', function ($event) use (&$dispatcher) {
84581 $dispatcher = $event->getDispatcher();
84582 });
84583 $this->dispatcher->dispatch('test');
84584 $this->assertSame($this->dispatcher, $dispatcher);
84585 }
84586
84587 /**
84588 * @see https://bugs.php.net/bug.php?id=62976
84589 *
84590 * This bug affects:
84591 * - The PHP 5.3 branch for versions < 5.3.18
84592 * - The PHP 5.4 branch for versions < 5.4.8
84593 * - The PHP 5.5 branch is not affected
84594 */
84595 public function testWorkaroundForPhpBug62976()
84596 {
84597 $dispatcher = new EventDispatcher();
84598 $dispatcher->addListener('bug.62976', new CallableClass());
84599 $dispatcher->removeListener('bug.62976', function() {});
84600 $this->assertTrue($dispatcher->hasListeners('bug.62976'));
84601 }
84602 }
84603
84604 class CallableClass
84605 {
84606 public function __invoke()
84607 {
84608 }
84609 }
84610
84611 class TestEventListener
84612 {
84613 public $preFooInvoked = false;
84614 public $postFooInvoked = false;
84615
84616 /* Listener methods */
84617
84618 public function preFoo(Event $e)
84619 {
84620 $this->preFooInvoked = true;
84621 }
84622
84623 public function postFoo(Event $e)
84624 {
84625 $this->postFooInvoked = true;
84626
84627 $e->stopPropagation();
84628 }
84629 }
84630
84631 class TestEventSubscriber implements EventSubscriberInterface
84632 {
84633 public static function getSubscribedEvents()
84634 {
84635 return array('pre.foo' => 'preFoo', 'post.foo' => 'postFoo');
84636 }
84637 }
84638
84639 class TestEventSubscriberWithPriorities implements EventSubscriberInterface
84640 {
84641 public static function getSubscribedEvents()
84642 {
84643 return array(
84644 'pre.foo' => array('preFoo', 10),
84645 'post.foo' => array('postFoo'),
84646 );
84647 }
84648 }
84649
84650 class TestEventSubscriberWithMultipleListeners implements EventSubscriberInterface
84651 {
84652 public static function getSubscribedEvents()
84653 {
84654 return array('pre.foo' => array(
84655 array('preFoo1'),
84656 array('preFoo2', 10)
84657 ));
84658 }
84659 }
84660 <?php
84661
84662 /*
84663 * This file is part of the Symfony package.
84664 *
84665 * (c) Fabien Potencier <fabien@symfony.com>
84666 *
84667 * For the full copyright and license information, please view the LICENSE
84668 * file that was distributed with this source code.
84669 */
84670
84671 namespace Symfony\Component\EventDispatcher\Tests;
84672
84673 use Symfony\Component\DependencyInjection\Container;
84674 use Symfony\Component\DependencyInjection\Scope;
84675 use Symfony\Component\EventDispatcher\ContainerAwareEventDispatcher;
84676 use Symfony\Component\EventDispatcher\Event;
84677 use Symfony\Component\EventDispatcher\EventSubscriberInterface;
84678
84679 class ContainerAwareEventDispatcherTest extends \PHPUnit_Framework_TestCase
84680 {
84681 protected function setUp()
84682 {
84683 if (!class_exists('Symfony\Component\DependencyInjection\Container')) {
84684 $this->markTestSkipped('The "DependencyInjection" component is not available');
84685 }
84686 }
84687
84688 public function testAddAListenerService()
84689 {
84690 $event = new Event();
84691
84692 $service = $this->getMock('Symfony\Component\EventDispatcher\Tests\Service');
84693
84694 $service
84695 ->expects($this->once())
84696 ->method('onEvent')
84697 ->with($event)
84698 ;
84699
84700 $container = new Container();
84701 $container->set('service.listener', $service);
84702
84703 $dispatcher = new ContainerAwareEventDispatcher($container);
84704 $dispatcher->addListenerService('onEvent', array('service.listener', 'onEvent'));
84705
84706 $dispatcher->dispatch('onEvent', $event);
84707 }
84708
84709 public function testAddASubscriberService()
84710 {
84711 $event = new Event();
84712
84713 $service = $this->getMock('Symfony\Component\EventDispatcher\Tests\SubscriberService');
84714
84715 $service
84716 ->expects($this->once())
84717 ->method('onEvent')
84718 ->with($event)
84719 ;
84720
84721 $container = new Container();
84722 $container->set('service.subscriber', $service);
84723
84724 $dispatcher = new ContainerAwareEventDispatcher($container);
84725 $dispatcher->addSubscriberService('service.subscriber', 'Symfony\Component\EventDispatcher\Tests\SubscriberService');
84726
84727 $dispatcher->dispatch('onEvent', $event);
84728 }
84729
84730 public function testPreventDuplicateListenerService()
84731 {
84732 $event = new Event();
84733
84734 $service = $this->getMock('Symfony\Component\EventDispatcher\Tests\Service');
84735
84736 $service
84737 ->expects($this->once())
84738 ->method('onEvent')
84739 ->with($event)
84740 ;
84741
84742 $container = new Container();
84743 $container->set('service.listener', $service);
84744
84745 $dispatcher = new ContainerAwareEventDispatcher($container);
84746 $dispatcher->addListenerService('onEvent', array('service.listener', 'onEvent'), 5);
84747 $dispatcher->addListenerService('onEvent', array('service.listener', 'onEvent'), 10);
84748
84749 $dispatcher->dispatch('onEvent', $event);
84750 }
84751
84752 /**
84753 * @expectedException \InvalidArgumentException
84754 */
84755 public function testTriggerAListenerServiceOutOfScope()
84756 {
84757 $service = $this->getMock('Symfony\Component\EventDispatcher\Tests\Service');
84758
84759 $scope = new Scope('scope');
84760 $container = new Container();
84761 $container->addScope($scope);
84762 $container->enterScope('scope');
84763
84764 $container->set('service.listener', $service, 'scope');
84765
84766 $dispatcher = new ContainerAwareEventDispatcher($container);
84767 $dispatcher->addListenerService('onEvent', array('service.listener', 'onEvent'));
84768
84769 $container->leaveScope('scope');
84770 $dispatcher->dispatch('onEvent');
84771 }
84772
84773 public function testReEnteringAScope()
84774 {
84775 $event = new Event();
84776
84777 $service1 = $this->getMock('Symfony\Component\EventDispatcher\Tests\Service');
84778
84779 $service1
84780 ->expects($this->exactly(2))
84781 ->method('onEvent')
84782 ->with($event)
84783 ;
84784
84785 $scope = new Scope('scope');
84786 $container = new Container();
84787 $container->addScope($scope);
84788 $container->enterScope('scope');
84789
84790 $container->set('service.listener', $service1, 'scope');
84791
84792 $dispatcher = new ContainerAwareEventDispatcher($container);
84793 $dispatcher->addListenerService('onEvent', array('service.listener', 'onEvent'));
84794 $dispatcher->dispatch('onEvent', $event);
84795
84796 $service2 = $this->getMock('Symfony\Component\EventDispatcher\Tests\Service');
84797
84798 $service2
84799 ->expects($this->once())
84800 ->method('onEvent')
84801 ->with($event)
84802 ;
84803
84804 $container->enterScope('scope');
84805 $container->set('service.listener', $service2, 'scope');
84806
84807 $dispatcher->dispatch('onEvent', $event);
84808
84809 $container->leaveScope('scope');
84810
84811 $dispatcher->dispatch('onEvent');
84812 }
84813
84814 public function testHasListenersOnLazyLoad()
84815 {
84816 $event = new Event();
84817
84818 $service = $this->getMock('Symfony\Component\EventDispatcher\Tests\Service');
84819
84820 $container = new Container();
84821 $container->set('service.listener', $service);
84822
84823 $dispatcher = new ContainerAwareEventDispatcher($container);
84824 $dispatcher->addListenerService('onEvent', array('service.listener', 'onEvent'));
84825
84826 $event->setDispatcher($dispatcher);
84827 $event->setName('onEvent');
84828
84829 $service
84830 ->expects($this->once())
84831 ->method('onEvent')
84832 ->with($event)
84833 ;
84834
84835 $this->assertTrue($dispatcher->hasListeners());
84836
84837 if ($dispatcher->hasListeners('onEvent')) {
84838 $dispatcher->dispatch('onEvent');
84839 }
84840 }
84841
84842 public function testGetListenersOnLazyLoad()
84843 {
84844 $event = new Event();
84845
84846 $service = $this->getMock('Symfony\Component\EventDispatcher\Tests\Service');
84847
84848 $container = new Container();
84849 $container->set('service.listener', $service);
84850
84851 $dispatcher = new ContainerAwareEventDispatcher($container);
84852 $dispatcher->addListenerService('onEvent', array('service.listener', 'onEvent'));
84853
84854 $listeners = $dispatcher->getListeners();
84855
84856 $this->assertTrue(isset($listeners['onEvent']));
84857
84858 $this->assertCount(1, $dispatcher->getListeners('onEvent'));
84859 }
84860
84861 public function testRemoveAfterDispatch()
84862 {
84863 $event = new Event();
84864
84865 $service = $this->getMock('Symfony\Component\EventDispatcher\Tests\Service');
84866
84867 $container = new Container();
84868 $container->set('service.listener', $service);
84869
84870 $dispatcher = new ContainerAwareEventDispatcher($container);
84871 $dispatcher->addListenerService('onEvent', array('service.listener', 'onEvent'));
84872
84873 $dispatcher->dispatch('onEvent', new Event());
84874 $dispatcher->removeListener('onEvent', array($container->get('service.listener'), 'onEvent'));
84875 $this->assertFalse($dispatcher->hasListeners('onEvent'));
84876 }
84877
84878 public function testRemoveBeforeDispatch()
84879 {
84880 $event = new Event();
84881
84882 $service = $this->getMock('Symfony\Component\EventDispatcher\Tests\Service');
84883
84884 $container = new Container();
84885 $container->set('service.listener', $service);
84886
84887 $dispatcher = new ContainerAwareEventDispatcher($container);
84888 $dispatcher->addListenerService('onEvent', array('service.listener', 'onEvent'));
84889
84890 $dispatcher->removeListener('onEvent', array($container->get('service.listener'), 'onEvent'));
84891 $this->assertFalse($dispatcher->hasListeners('onEvent'));
84892 }
84893 }
84894
84895 class Service
84896 {
84897 public function onEvent(Event $e)
84898 {
84899 }
84900 }
84901
84902 class SubscriberService implements EventSubscriberInterface
84903 {
84904 public static function getSubscribedEvents()
84905 {
84906 return array(
84907 'onEvent' => 'onEvent',
84908 'onEvent' => array('onEvent', 10),
84909 'onEvent' => array('onEvent'),
84910 );
84911 }
84912
84913 public function onEvent(Event $e)
84914 {
84915 }
84916 }
84917 <?php
84918
84919 /*
84920 * This file is part of the Symfony package.
84921 *
84922 * (c) Fabien Potencier <fabien@symfony.com>
84923 *
84924 * For the full copyright and license information, please view the LICENSE
84925 * file that was distributed with this source code.
84926 */
84927
84928 namespace Symfony\Component\EventDispatcher\Tests;
84929
84930 use Symfony\Component\EventDispatcher\Event;
84931 use Symfony\Component\EventDispatcher\ImmutableEventDispatcher;
84932 use Symfony\Component\EventDispatcher\EventSubscriberInterface;
84933
84934 /**
84935 * @author Bernhard Schussek <bschussek@gmail.com>
84936 */
84937 class ImmutableEventDispatcherTest extends \PHPUnit_Framework_TestCase
84938 {
84939 /**
84940 * @var \PHPUnit_Framework_MockObject_MockObject
84941 */
84942 private $innerDispatcher;
84943
84944 /**
84945 * @var ImmutableEventDispatcher
84946 */
84947 private $dispatcher;
84948
84949 protected function setUp()
84950 {
84951 $this->innerDispatcher = $this->getMock('Symfony\Component\EventDispatcher\EventDispatcherInterface');
84952 $this->dispatcher = new ImmutableEventDispatcher($this->innerDispatcher);
84953 }
84954
84955 public function testDispatchDelegates()
84956 {
84957 $event = new Event();
84958
84959 $this->innerDispatcher->expects($this->once())
84960 ->method('dispatch')
84961 ->with('event', $event)
84962 ->will($this->returnValue('result'));
84963
84964 $this->assertSame('result', $this->dispatcher->dispatch('event', $event));
84965 }
84966
84967 public function testGetListenersDelegates()
84968 {
84969 $this->innerDispatcher->expects($this->once())
84970 ->method('getListeners')
84971 ->with('event')
84972 ->will($this->returnValue('result'));
84973
84974 $this->assertSame('result', $this->dispatcher->getListeners('event'));
84975 }
84976
84977 public function testHasListenersDelegates()
84978 {
84979 $this->innerDispatcher->expects($this->once())
84980 ->method('hasListeners')
84981 ->with('event')
84982 ->will($this->returnValue('result'));
84983
84984 $this->assertSame('result', $this->dispatcher->hasListeners('event'));
84985 }
84986
84987 /**
84988 * @expectedException \BadMethodCallException
84989 */
84990 public function testAddListenerDisallowed()
84991 {
84992 $this->dispatcher->addListener('event', function () { return 'foo'; });
84993 }
84994
84995 /**
84996 * @expectedException \BadMethodCallException
84997 */
84998 public function testAddSubscriberDisallowed()
84999 {
85000 $subscriber = $this->getMock('Symfony\Component\EventDispatcher\EventSubscriberInterface');
85001
85002 $this->dispatcher->addSubscriber($subscriber);
85003 }
85004
85005 /**
85006 * @expectedException \BadMethodCallException
85007 */
85008 public function testRemoveListenerDisallowed()
85009 {
85010 $this->dispatcher->removeListener('event', function () { return 'foo'; });
85011 }
85012
85013 /**
85014 * @expectedException \BadMethodCallException
85015 */
85016 public function testRemoveSubscriberDisallowed()
85017 {
85018 $subscriber = $this->getMock('Symfony\Component\EventDispatcher\EventSubscriberInterface');
85019
85020 $this->dispatcher->removeSubscriber($subscriber);
85021 }
85022 }
85023 EventDispatcher Component
85024 =========================
85025
85026 EventDispatcher implements a lightweight version of the Observer design
85027 pattern.
85028
85029 use Symfony\Component\EventDispatcher\EventDispatcher;
85030 use Symfony\Component\EventDispatcher\Event;
85031
85032 $dispatcher = new EventDispatcher();
85033
85034 $dispatcher->addListener('event_name', function (Event $event) {
85035 // ...
85036 });
85037
85038 $dispatcher->dispatch('event_name');
85039
85040 Resources
85041 ---------
85042
85043 You can run the unit tests with the following command:
85044
85045 $ cd path/to/Symfony/Component/EventDispatcher/
85046 $ composer.phar install --dev
85047 $ phpunit
85048 <?php
85049
85050 /*
85051 * This file is part of the Symfony package.
85052 *
85053 * (c) Fabien Potencier <fabien@symfony.com>
85054 *
85055 * For the full copyright and license information, please view the LICENSE
85056 * file that was distributed with this source code.
85057 */
85058
85059 namespace Symfony\Component\EventDispatcher;
85060
85061 /**
85062 * The EventDispatcherInterface is the central point of Symfony's event listener system.
85063 * Listeners are registered on the manager and events are dispatched through the
85064 * manager.
85065 *
85066 * @author Bernhard Schussek <bschussek@gmail.com>
85067 *
85068 * @api
85069 */
85070 interface EventDispatcherInterface
85071 {
85072 /**
85073 * Dispatches an event to all registered listeners.
85074 *
85075 * @param string $eventName The name of the event to dispatch. The name of
85076 * the event is the name of the method that is
85077 * invoked on listeners.
85078 * @param Event $event The event to pass to the event handlers/listeners.
85079 * If not supplied, an empty Event instance is created.
85080 *
85081 * @return Event
85082 *
85083 * @api
85084 */
85085 public function dispatch($eventName, Event $event = null);
85086
85087 /**
85088 * Adds an event listener that listens on the specified events.
85089 *
85090 * @param string $eventName The event to listen on
85091 * @param callable $listener The listener
85092 * @param integer $priority The higher this value, the earlier an event
85093 * listener will be triggered in the chain (defaults to 0)
85094 *
85095 * @api
85096 */
85097 public function addListener($eventName, $listener, $priority = 0);
85098
85099 /**
85100 * Adds an event subscriber.
85101 *
85102 * The subscriber is asked for all the events he is
85103 * interested in and added as a listener for these events.
85104 *
85105 * @param EventSubscriberInterface $subscriber The subscriber.
85106 *
85107 * @api
85108 */
85109 public function addSubscriber(EventSubscriberInterface $subscriber);
85110
85111 /**
85112 * Removes an event listener from the specified events.
85113 *
85114 * @param string|array $eventName The event(s) to remove a listener from
85115 * @param callable $listener The listener to remove
85116 */
85117 public function removeListener($eventName, $listener);
85118
85119 /**
85120 * Removes an event subscriber.
85121 *
85122 * @param EventSubscriberInterface $subscriber The subscriber
85123 */
85124 public function removeSubscriber(EventSubscriberInterface $subscriber);
85125
85126 /**
85127 * Gets the listeners of a specific event or all listeners.
85128 *
85129 * @param string $eventName The name of the event
85130 *
85131 * @return array The event listeners for the specified event, or all event listeners by event name
85132 */
85133 public function getListeners($eventName = null);
85134
85135 /**
85136 * Checks whether an event has any registered listeners.
85137 *
85138 * @param string $eventName The name of the event
85139 *
85140 * @return Boolean true if the specified event has any listeners, false otherwise
85141 */
85142 public function hasListeners($eventName = null);
85143 }
85144 Copyright (c) 2004-2013 Fabien Potencier
85145
85146 Permission is hereby granted, free of charge, to any person obtaining a copy
85147 of this software and associated documentation files (the "Software"), to deal
85148 in the Software without restriction, including without limitation the rights
85149 to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
85150 copies of the Software, and to permit persons to whom the Software is furnished
85151 to do so, subject to the following conditions:
85152
85153 The above copyright notice and this permission notice shall be included in all
85154 copies or substantial portions of the Software.
85155
85156 THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
85157 IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
85158 FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
85159 AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
85160 LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
85161 OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
85162 THE SOFTWARE.
85163 <?php
85164
85165 /*
85166 * This file is part of the Symfony package.
85167 *
85168 * (c) Fabien Potencier <fabien@symfony.com>
85169 *
85170 * For the full copyright and license information, please view the LICENSE
85171 * file that was distributed with this source code.
85172 */
85173
85174 namespace Symfony\Component\EventDispatcher;
85175
85176 /**
85177 * Event is the base class for classes containing event data.
85178 *
85179 * This class contains no event data. It is used by events that do not pass
85180 * state information to an event handler when an event is raised.
85181 *
85182 * You can call the method stopPropagation() to abort the execution of
85183 * further listeners in your event listener.
85184 *
85185 * @author Guilherme Blanco <guilhermeblanco@hotmail.com>
85186 * @author Jonathan Wage <jonwage@gmail.com>
85187 * @author Roman Borschel <roman@code-factory.org>
85188 * @author Bernhard Schussek <bschussek@gmail.com>
85189 *
85190 * @api
85191 */
85192 class Event
85193 {
85194 /**
85195 * @var Boolean Whether no further event listeners should be triggered
85196 */
85197 private $propagationStopped = false;
85198
85199 /**
85200 * @var EventDispatcher Dispatcher that dispatched this event
85201 */
85202 private $dispatcher;
85203
85204 /**
85205 * @var string This event's name
85206 */
85207 private $name;
85208
85209 /**
85210 * Returns whether further event listeners should be triggered.
85211 *
85212 * @see Event::stopPropagation
85213 * @return Boolean Whether propagation was already stopped for this event.
85214 *
85215 * @api
85216 */
85217 public function isPropagationStopped()
85218 {
85219 return $this->propagationStopped;
85220 }
85221
85222 /**
85223 * Stops the propagation of the event to further event listeners.
85224 *
85225 * If multiple event listeners are connected to the same event, no
85226 * further event listener will be triggered once any trigger calls
85227 * stopPropagation().
85228 *
85229 * @api
85230 */
85231 public function stopPropagation()
85232 {
85233 $this->propagationStopped = true;
85234 }
85235
85236 /**
85237 * Stores the EventDispatcher that dispatches this Event
85238 *
85239 * @param EventDispatcherInterface $dispatcher
85240 *
85241 * @api
85242 */
85243 public function setDispatcher(EventDispatcherInterface $dispatcher)
85244 {
85245 $this->dispatcher = $dispatcher;
85246 }
85247
85248 /**
85249 * Returns the EventDispatcher that dispatches this Event
85250 *
85251 * @return EventDispatcherInterface
85252 *
85253 * @api
85254 */
85255 public function getDispatcher()
85256 {
85257 return $this->dispatcher;
85258 }
85259
85260 /**
85261 * Gets the event's name.
85262 *
85263 * @return string
85264 *
85265 * @api
85266 */
85267 public function getName()
85268 {
85269 return $this->name;
85270 }
85271
85272 /**
85273 * Sets the event's name property.
85274 *
85275 * @param string $name The event name.
85276 *
85277 * @api
85278 */
85279 public function setName($name)
85280 {
85281 $this->name = $name;
85282 }
85283 }
85284 <?php
85285
85286 /*
85287 * This file is part of the Symfony package.
85288 *
85289 * (c) Fabien Potencier <fabien@symfony.com>
85290 *
85291 * For the full copyright and license information, please view the LICENSE
85292 * file that was distributed with this source code.
85293 */
85294
85295 namespace Symfony\Component\EventDispatcher;
85296
85297 /**
85298 * The EventDispatcherInterface is the central point of Symfony's event listener system.
85299 *
85300 * Listeners are registered on the manager and events are dispatched through the
85301 * manager.
85302 *
85303 * @author Guilherme Blanco <guilhermeblanco@hotmail.com>
85304 * @author Jonathan Wage <jonwage@gmail.com>
85305 * @author Roman Borschel <roman@code-factory.org>
85306 * @author Bernhard Schussek <bschussek@gmail.com>
85307 * @author Fabien Potencier <fabien@symfony.com>
85308 * @author Jordi Boggiano <j.boggiano@seld.be>
85309 * @author Jordan Alliot <jordan.alliot@gmail.com>
85310 *
85311 * @api
85312 */
85313 class EventDispatcher implements EventDispatcherInterface
85314 {
85315 private $listeners = array();
85316 private $sorted = array();
85317
85318 /**
85319 * @see EventDispatcherInterface::dispatch
85320 *
85321 * @api
85322 */
85323 public function dispatch($eventName, Event $event = null)
85324 {
85325 if (null === $event) {
85326 $event = new Event();
85327 }
85328
85329 $event->setDispatcher($this);
85330 $event->setName($eventName);
85331
85332 if (!isset($this->listeners[$eventName])) {
85333 return $event;
85334 }
85335
85336 $this->doDispatch($this->getListeners($eventName), $eventName, $event);
85337
85338 return $event;
85339 }
85340
85341 /**
85342 * @see EventDispatcherInterface::getListeners
85343 */
85344 public function getListeners($eventName = null)
85345 {
85346 if (null !== $eventName) {
85347 if (!isset($this->sorted[$eventName])) {
85348 $this->sortListeners($eventName);
85349 }
85350
85351 return $this->sorted[$eventName];
85352 }
85353
85354 foreach (array_keys($this->listeners) as $eventName) {
85355 if (!isset($this->sorted[$eventName])) {
85356 $this->sortListeners($eventName);
85357 }
85358 }
85359
85360 return $this->sorted;
85361 }
85362
85363 /**
85364 * @see EventDispatcherInterface::hasListeners
85365 */
85366 public function hasListeners($eventName = null)
85367 {
85368 return (Boolean) count($this->getListeners($eventName));
85369 }
85370
85371 /**
85372 * @see EventDispatcherInterface::addListener
85373 *
85374 * @api
85375 */
85376 public function addListener($eventName, $listener, $priority = 0)
85377 {
85378 $this->listeners[$eventName][$priority][] = $listener;
85379 unset($this->sorted[$eventName]);
85380 }
85381
85382 /**
85383 * @see EventDispatcherInterface::removeListener
85384 */
85385 public function removeListener($eventName, $listener)
85386 {
85387 if (!isset($this->listeners[$eventName])) {
85388 return;
85389 }
85390
85391 foreach ($this->listeners[$eventName] as $priority => $listeners) {
85392 if (false !== ($key = array_search($listener, $listeners, true))) {
85393 unset($this->listeners[$eventName][$priority][$key], $this->sorted[$eventName]);
85394 }
85395 }
85396 }
85397
85398 /**
85399 * @see EventDispatcherInterface::addSubscriber
85400 *
85401 * @api
85402 */
85403 public function addSubscriber(EventSubscriberInterface $subscriber)
85404 {
85405 foreach ($subscriber->getSubscribedEvents() as $eventName => $params) {
85406 if (is_string($params)) {
85407 $this->addListener($eventName, array($subscriber, $params));
85408 } elseif (is_string($params[0])) {
85409 $this->addListener($eventName, array($subscriber, $params[0]), isset($params[1]) ? $params[1] : 0);
85410 } else {
85411 foreach ($params as $listener) {
85412 $this->addListener($eventName, array($subscriber, $listener[0]), isset($listener[1]) ? $listener[1] : 0);
85413 }
85414 }
85415 }
85416 }
85417
85418 /**
85419 * @see EventDispatcherInterface::removeSubscriber
85420 */
85421 public function removeSubscriber(EventSubscriberInterface $subscriber)
85422 {
85423 foreach ($subscriber->getSubscribedEvents() as $eventName => $params) {
85424 if (is_array($params) && is_array($params[0])) {
85425 foreach ($params as $listener) {
85426 $this->removeListener($eventName, array($subscriber, $listener[0]));
85427 }
85428 } else {
85429 $this->removeListener($eventName, array($subscriber, is_string($params) ? $params : $params[0]));
85430 }
85431 }
85432 }
85433
85434 /**
85435 * Triggers the listeners of an event.
85436 *
85437 * This method can be overridden to add functionality that is executed
85438 * for each listener.
85439 *
85440 * @param array[callback] $listeners The event listeners.
85441 * @param string $eventName The name of the event to dispatch.
85442 * @param Event $event The event object to pass to the event handlers/listeners.
85443 */
85444 protected function doDispatch($listeners, $eventName, Event $event)
85445 {
85446 foreach ($listeners as $listener) {
85447 call_user_func($listener, $event);
85448 if ($event->isPropagationStopped()) {
85449 break;
85450 }
85451 }
85452 }
85453
85454 /**
85455 * Sorts the internal list of listeners for the given event by priority.
85456 *
85457 * @param string $eventName The name of the event.
85458 */
85459 private function sortListeners($eventName)
85460 {
85461 $this->sorted[$eventName] = array();
85462
85463 if (isset($this->listeners[$eventName])) {
85464 krsort($this->listeners[$eventName]);
85465 $this->sorted[$eventName] = call_user_func_array('array_merge', $this->listeners[$eventName]);
85466 }
85467 }
85468 }
85469 <?php
85470
85471 /*
85472 * This file is part of the Symfony package.
85473 *
85474 * (c) Fabien Potencier <fabien@symfony.com>
85475 *
85476 * For the full copyright and license information, please view the LICENSE
85477 * file that was distributed with this source code.
85478 */
85479
85480 namespace Symfony\Component\EventDispatcher;
85481
85482 /**
85483 * Event encapsulation class.
85484 *
85485 * Encapsulates events thus decoupling the observer from the subject they encapsulate.
85486 *
85487 * @author Drak <drak@zikula.org>
85488 */
85489 class GenericEvent extends Event implements \ArrayAccess, \IteratorAggregate
85490 {
85491 /**
85492 * Observer pattern subject.
85493 *
85494 * @var mixed usually object or callable
85495 */
85496 protected $subject;
85497
85498 /**
85499 * Array of arguments.
85500 *
85501 * @var array
85502 */
85503 protected $arguments;
85504
85505 /**
85506 * Encapsulate an event with $subject and $args.
85507 *
85508 * @param mixed $subject The subject of the event, usually an object.
85509 * @param array $arguments Arguments to store in the event.
85510 */
85511 public function __construct($subject = null, array $arguments = array())
85512 {
85513 $this->subject = $subject;
85514 $this->arguments = $arguments;
85515 }
85516
85517 /**
85518 * Getter for subject property.
85519 *
85520 * @return mixed $subject The observer subject.
85521 */
85522 public function getSubject()
85523 {
85524 return $this->subject;
85525 }
85526
85527 /**
85528 * Get argument by key.
85529 *
85530 * @param string $key Key.
85531 *
85532 * @throws \InvalidArgumentException If key is not found.
85533 *
85534 * @return mixed Contents of array key.
85535 */
85536 public function getArgument($key)
85537 {
85538 if ($this->hasArgument($key)) {
85539 return $this->arguments[$key];
85540 }
85541
85542 throw new \InvalidArgumentException(sprintf('%s not found in %s', $key, $this->getName()));
85543 }
85544
85545 /**
85546 * Add argument to event.
85547 *
85548 * @param string $key Argument name.
85549 * @param mixed $value Value.
85550 *
85551 * @return GenericEvent
85552 */
85553 public function setArgument($key, $value)
85554 {
85555 $this->arguments[$key] = $value;
85556
85557 return $this;
85558 }
85559
85560 /**
85561 * Getter for all arguments.
85562 *
85563 * @return array
85564 */
85565 public function getArguments()
85566 {
85567 return $this->arguments;
85568 }
85569
85570 /**
85571 * Set args property.
85572 *
85573 * @param array $args Arguments.
85574 *
85575 * @return GenericEvent
85576 */
85577 public function setArguments(array $args = array())
85578 {
85579 $this->arguments = $args;
85580
85581 return $this;
85582 }
85583
85584 /**
85585 * Has argument.
85586 *
85587 * @param string $key Key of arguments array.
85588 *
85589 * @return boolean
85590 */
85591 public function hasArgument($key)
85592 {
85593 return array_key_exists($key, $this->arguments);
85594 }
85595
85596 /**
85597 * ArrayAccess for argument getter.
85598 *
85599 * @param string $key Array key.
85600 *
85601 * @throws \InvalidArgumentException If key does not exist in $this->args.
85602 *
85603 * @return mixed
85604 */
85605 public function offsetGet($key)
85606 {
85607 return $this->getArgument($key);
85608 }
85609
85610 /**
85611 * ArrayAccess for argument setter.
85612 *
85613 * @param string $key Array key to set.
85614 * @param mixed $value Value.
85615 */
85616 public function offsetSet($key, $value)
85617 {
85618 $this->setArgument($key, $value);
85619 }
85620
85621 /**
85622 * ArrayAccess for unset argument.
85623 *
85624 * @param string $key Array key.
85625 */
85626 public function offsetUnset($key)
85627 {
85628 if ($this->hasArgument($key)) {
85629 unset($this->arguments[$key]);
85630 }
85631 }
85632
85633 /**
85634 * ArrayAccess has argument.
85635 *
85636 * @param string $key Array key.
85637 *
85638 * @return boolean
85639 */
85640 public function offsetExists($key)
85641 {
85642 return $this->hasArgument($key);
85643 }
85644
85645 /**
85646 * IteratorAggregate for iterating over the object like an array
85647 *
85648 * @return \ArrayIterator
85649 */
85650 public function getIterator()
85651 {
85652 return new \ArrayIterator($this->arguments);
85653 }
85654 }
85655 <?php
85656
85657 /*
85658 * This file is part of the Symfony package.
85659 *
85660 * (c) Fabien Potencier <fabien@symfony.com>
85661 *
85662 * For the full copyright and license information, please view the LICENSE
85663 * file that was distributed with this source code.
85664 */
85665
85666 namespace Symfony\Component\EventDispatcher\Debug;
85667
85668 /**
85669 * @author Fabien Potencier <fabien@symfony.com>
85670 */
85671 interface TraceableEventDispatcherInterface
85672 {
85673 /**
85674 * Gets the called listeners.
85675 *
85676 * @return array An array of called listeners
85677 */
85678 public function getCalledListeners();
85679
85680 /**
85681 * Gets the not called listeners.
85682 *
85683 * @return array An array of not called listeners
85684 */
85685 public function getNotCalledListeners();
85686 }
85687 CHANGELOG
85688 =========
85689
85690 2.1.0
85691 -----
85692
85693 * added TraceableEventDispatcherInterface
85694 * added ContainerAwareEventDispatcher
85695 * added a reference to the EventDispatcher on the Event
85696 * added a reference to the Event name on the event
85697 * added fluid interface to the dispatch() method which now returns the Event
85698 object
85699 * added GenericEvent event class
85700 * added the possibility for subscribers to subscribe several times for the
85701 same event
85702 * added ImmutableEventDispatcher
85703 {
85704 "name": "symfony/event-dispatcher",
85705 "type": "library",
85706 "description": "Symfony EventDispatcher Component",
85707 "keywords": [],
85708 "homepage": "http://symfony.com",
85709 "license": "MIT",
85710 "authors": [
85711 {
85712 "name": "Fabien Potencier",
85713 "email": "fabien@symfony.com"
85714 },
85715 {
85716 "name": "Symfony Community",
85717 "homepage": "http://symfony.com/contributors"
85718 }
85719 ],
85720 "require": {
85721 "php": ">=5.3.3"
85722 },
85723 "require-dev": {
85724 "symfony/dependency-injection": "~2.0"
85725 },
85726 "suggest": {
85727 "symfony/dependency-injection": "",
85728 "symfony/http-kernel": ""
85729 },
85730 "autoload": {
85731 "psr-0": { "Symfony\\Component\\EventDispatcher\\": "" }
85732 },
85733 "target-dir": "Symfony/Component/EventDispatcher",
85734 "minimum-stability": "dev",
85735 "extra": {
85736 "branch-alias": {
85737 "dev-master": "2.3-dev"
85738 }
85739 }
85740 }
85741 <?php
85742
85743 /*
85744 * This file is part of the Symfony package.
85745 *
85746 * (c) Fabien Potencier <fabien@symfony.com>
85747 *
85748 * For the full copyright and license information, please view the LICENSE
85749 * file that was distributed with this source code.
85750 */
85751
85752 namespace Symfony\Component\EventDispatcher;
85753
85754 use Symfony\Component\DependencyInjection\ContainerInterface;
85755
85756 /**
85757 * Lazily loads listeners and subscribers from the dependency injection
85758 * container
85759 *
85760 * @author Fabien Potencier <fabien@symfony.com>
85761 * @author Bernhard Schussek <bschussek@gmail.com>
85762 * @author Jordan Alliot <jordan.alliot@gmail.com>
85763 */
85764 class ContainerAwareEventDispatcher extends EventDispatcher
85765 {
85766 /**
85767 * The container from where services are loaded
85768 * @var ContainerInterface
85769 */
85770 private $container;
85771
85772 /**
85773 * The service IDs of the event listeners and subscribers
85774 * @var array
85775 */
85776 private $listenerIds = array();
85777
85778 /**
85779 * The services registered as listeners
85780 * @var array
85781 */
85782 private $listeners = array();
85783
85784 /**
85785 * Constructor.
85786 *
85787 * @param ContainerInterface $container A ContainerInterface instance
85788 */
85789 public function __construct(ContainerInterface $container)
85790 {
85791 $this->container = $container;
85792 }
85793
85794 /**
85795 * Adds a service as event listener
85796 *
85797 * @param string $eventName Event for which the listener is added
85798 * @param array $callback The service ID of the listener service & the method
85799 * name that has to be called
85800 * @param integer $priority The higher this value, the earlier an event listener
85801 * will be triggered in the chain.
85802 * Defaults to 0.
85803 *
85804 * @throws \InvalidArgumentException
85805 */
85806 public function addListenerService($eventName, $callback, $priority = 0)
85807 {
85808 if (!is_array($callback) || 2 !== count($callback)) {
85809 throw new \InvalidArgumentException('Expected an array("service", "method") argument');
85810 }
85811
85812 $this->listenerIds[$eventName][] = array($callback[0], $callback[1], $priority);
85813 }
85814
85815 public function removeListener($eventName, $listener)
85816 {
85817 $this->lazyLoad($eventName);
85818
85819 if (isset($this->listeners[$eventName])) {
85820 foreach ($this->listeners[$eventName] as $key => $l) {
85821 foreach ($this->listenerIds[$eventName] as $i => $args) {
85822 list($serviceId, $method, $priority) = $args;
85823 if ($key === $serviceId.'.'.$method) {
85824 if ($listener === array($l, $method)) {
85825 unset($this->listeners[$eventName][$key]);
85826 if (empty($this->listeners[$eventName])) {
85827 unset($this->listeners[$eventName]);
85828 }
85829 unset($this->listenerIds[$eventName][$i]);
85830 if (empty($this->listenerIds[$eventName])) {
85831 unset($this->listenerIds[$eventName]);
85832 }
85833 }
85834 }
85835 }
85836 }
85837 }
85838
85839 parent::removeListener($eventName, $listener);
85840 }
85841
85842 /**
85843 * @see EventDispatcherInterface::hasListeners
85844 */
85845 public function hasListeners($eventName = null)
85846 {
85847 if (null === $eventName) {
85848 return (Boolean) count($this->listenerIds) || (Boolean) count($this->listeners);
85849 }
85850
85851 if (isset($this->listenerIds[$eventName])) {
85852 return true;
85853 }
85854
85855 return parent::hasListeners($eventName);
85856 }
85857
85858 /**
85859 * @see EventDispatcherInterface::getListeners
85860 */
85861 public function getListeners($eventName = null)
85862 {
85863 if (null === $eventName) {
85864 foreach (array_keys($this->listenerIds) as $serviceEventName) {
85865 $this->lazyLoad($serviceEventName);
85866 }
85867 } else {
85868 $this->lazyLoad($eventName);
85869 }
85870
85871 return parent::getListeners($eventName);
85872 }
85873
85874 /**
85875 * Adds a service as event subscriber
85876 *
85877 * @param string $serviceId The service ID of the subscriber service
85878 * @param string $class The service's class name (which must implement EventSubscriberInterface)
85879 */
85880 public function addSubscriberService($serviceId, $class)
85881 {
85882 foreach ($class::getSubscribedEvents() as $eventName => $params) {
85883 if (is_string($params)) {
85884 $this->listenerIds[$eventName][] = array($serviceId, $params, 0);
85885 } elseif (is_string($params[0])) {
85886 $this->listenerIds[$eventName][] = array($serviceId, $params[0], isset($params[1]) ? $params[1] : 0);
85887 } else {
85888 foreach ($params as $listener) {
85889 $this->listenerIds[$eventName][] = array($serviceId, $listener[0], isset($listener[1]) ? $listener[1] : 0);
85890 }
85891 }
85892 }
85893 }
85894
85895 /**
85896 * {@inheritDoc}
85897 *
85898 * Lazily loads listeners for this event from the dependency injection
85899 * container.
85900 *
85901 * @throws \InvalidArgumentException if the service is not defined
85902 */
85903 public function dispatch($eventName, Event $event = null)
85904 {
85905 $this->lazyLoad($eventName);
85906
85907 return parent::dispatch($eventName, $event);
85908 }
85909
85910 public function getContainer()
85911 {
85912 return $this->container;
85913 }
85914
85915 /**
85916 * Lazily loads listeners for this event from the dependency injection
85917 * container.
85918 *
85919 * @param string $eventName The name of the event to dispatch. The name of
85920 * the event is the name of the method that is
85921 * invoked on listeners.
85922 */
85923 protected function lazyLoad($eventName)
85924 {
85925 if (isset($this->listenerIds[$eventName])) {
85926 foreach ($this->listenerIds[$eventName] as $args) {
85927 list($serviceId, $method, $priority) = $args;
85928 $listener = $this->container->get($serviceId);
85929
85930 $key = $serviceId.'.'.$method;
85931 if (!isset($this->listeners[$eventName][$key])) {
85932 $this->addListener($eventName, array($listener, $method), $priority);
85933 } elseif ($listener !== $this->listeners[$eventName][$key]) {
85934 parent::removeListener($eventName, array($this->listeners[$eventName][$key], $method));
85935 $this->addListener($eventName, array($listener, $method), $priority);
85936 }
85937
85938 $this->listeners[$eventName][$key] = $listener;
85939 }
85940 }
85941 }
85942 }
85943 <?xml version="1.0" encoding="UTF-8"?>
85944
85945 <phpunit backupGlobals="false"
85946 backupStaticAttributes="false"
85947 colors="true"
85948 convertErrorsToExceptions="true"
85949 convertNoticesToExceptions="true"
85950 convertWarningsToExceptions="true"
85951 processIsolation="false"
85952 stopOnFailure="false"
85953 syntaxCheck="false"
85954 bootstrap="vendor/autoload.php"
85955 >
85956 <testsuites>
85957 <testsuite name="Symfony EventDispatcher Component Test Suite">
85958 <directory>./Tests/</directory>
85959 </testsuite>
85960 </testsuites>
85961
85962 <filter>
85963 <whitelist>
85964 <directory>./</directory>
85965 <exclude>
85966 <directory>./Resources</directory>
85967 <directory>./Tests</directory>
85968 <directory>./vendor</directory>
85969 </exclude>
85970 </whitelist>
85971 </filter>
85972 </phpunit>
85973 <?php
85974
85975 /*
85976 * This file is part of the Symfony package.
85977 *
85978 * (c) Fabien Potencier <fabien@symfony.com>
85979 *
85980 * For the full copyright and license information, please view the LICENSE
85981 * file that was distributed with this source code.
85982 */
85983
85984 namespace Symfony\Component\EventDispatcher;
85985
85986 /**
85987 * A read-only proxy for an event dispatcher.
85988 *
85989 * @author Bernhard Schussek <bschussek@gmail.com>
85990 */
85991 class ImmutableEventDispatcher implements EventDispatcherInterface
85992 {
85993 /**
85994 * The proxied dispatcher.
85995 * @var EventDispatcherInterface
85996 */
85997 private $dispatcher;
85998
85999 /**
86000 * Creates an unmodifiable proxy for an event dispatcher.
86001 *
86002 * @param EventDispatcherInterface $dispatcher The proxied event dispatcher.
86003 */
86004 public function __construct(EventDispatcherInterface $dispatcher)
86005 {
86006 $this->dispatcher = $dispatcher;
86007 }
86008
86009 /**
86010 * {@inheritdoc}
86011 */
86012 public function dispatch($eventName, Event $event = null)
86013 {
86014 return $this->dispatcher->dispatch($eventName, $event);
86015 }
86016
86017 /**
86018 * {@inheritdoc}
86019 */
86020 public function addListener($eventName, $listener, $priority = 0)
86021 {
86022 throw new \BadMethodCallException('Unmodifiable event dispatchers must not be modified.');
86023 }
86024
86025 /**
86026 * {@inheritdoc}
86027 */
86028 public function addSubscriber(EventSubscriberInterface $subscriber)
86029 {
86030 throw new \BadMethodCallException('Unmodifiable event dispatchers must not be modified.');
86031 }
86032
86033 /**
86034 * {@inheritdoc}
86035 */
86036 public function removeListener($eventName, $listener)
86037 {
86038 throw new \BadMethodCallException('Unmodifiable event dispatchers must not be modified.');
86039 }
86040
86041 /**
86042 * {@inheritdoc}
86043 */
86044 public function removeSubscriber(EventSubscriberInterface $subscriber)
86045 {
86046 throw new \BadMethodCallException('Unmodifiable event dispatchers must not be modified.');
86047 }
86048
86049 /**
86050 * {@inheritdoc}
86051 */
86052 public function getListeners($eventName = null)
86053 {
86054 return $this->dispatcher->getListeners($eventName);
86055 }
86056
86057 /**
86058 * {@inheritdoc}
86059 */
86060 public function hasListeners($eventName = null)
86061 {
86062 return $this->dispatcher->hasListeners($eventName);
86063 }
86064 }
86065 <?php
86066
86067 /*
86068 * This file is part of the Symfony package.
86069 *
86070 * (c) Fabien Potencier <fabien@symfony.com>
86071 *
86072 * For the full copyright and license information, please view the LICENSE
86073 * file that was distributed with this source code.
86074 */
86075
86076 namespace Symfony\Component\EventDispatcher;
86077
86078 /**
86079 * An EventSubscriber knows himself what events he is interested in.
86080 * If an EventSubscriber is added to an EventDispatcherInterface, the manager invokes
86081 * {@link getSubscribedEvents} and registers the subscriber as a listener for all
86082 * returned events.
86083 *
86084 * @author Guilherme Blanco <guilhermeblanco@hotmail.com>
86085 * @author Jonathan Wage <jonwage@gmail.com>
86086 * @author Roman Borschel <roman@code-factory.org>
86087 * @author Bernhard Schussek <bschussek@gmail.com>
86088 *
86089 * @api
86090 */
86091 interface EventSubscriberInterface
86092 {
86093 /**
86094 * Returns an array of event names this subscriber wants to listen to.
86095 *
86096 * The array keys are event names and the value can be:
86097 *
86098 * * The method name to call (priority defaults to 0)
86099 * * An array composed of the method name to call and the priority
86100 * * An array of arrays composed of the method names to call and respective
86101 * priorities, or 0 if unset
86102 *
86103 * For instance:
86104 *
86105 * * array('eventName' => 'methodName')
86106 * * array('eventName' => array('methodName', $priority))
86107 * * array('eventName' => array(array('methodName1', $priority), array('methodName2'))
86108 *
86109 * @return array The event names to listen to
86110 *
86111 * @api
86112 */
86113 public static function getSubscribedEvents();
86114 }
86115 <?php
86116
86117 /*
86118 * This file is part of the Symfony package.
86119 *
86120 * (c) Fabien Potencier <fabien@symfony.com>
86121 *
86122 * For the full copyright and license information, please view the LICENSE
86123 * file that was distributed with this source code.
86124 */
86125
86126 namespace Symfony\Component\Process\Tests;
86127
86128 use Symfony\Component\Process\ProcessUtils;
86129
86130 class ProcessUtilsTest extends \PHPUnit_Framework_TestCase
86131 {
86132 /**
86133 * @dataProvider dataArguments
86134 */
86135 public function testEscapeArgument($result, $argument)
86136 {
86137 $this->assertSame($result, ProcessUtils::escapeArgument($argument));
86138 }
86139
86140 public function dataArguments()
86141 {
86142 if (defined('PHP_WINDOWS_VERSION_BUILD')) {
86143 return array(
86144 array('"foo bar"', 'foo bar'),
86145 array('^%"path"^%', '%path%'),
86146 array('"<|>"\\"" "\\""\'f"', '<|>" "\'f'),
86147 array('""', ''),
86148 );
86149 }
86150
86151 return array(
86152 array("'foo bar'", 'foo bar'),
86153 array("'%path%'", '%path%'),
86154 array("'<|>\" \"'\\''f'", '<|>" "\'f'),
86155 array("''", ''),
86156 );
86157 }
86158 }
86159 <?php
86160
86161 /*
86162 * This file is part of the Symfony package.
86163 *
86164 * (c) Fabien Potencier <fabien@symfony.com>
86165 *
86166 * For the full copyright and license information, please view the LICENSE
86167 * file that was distributed with this source code.
86168 */
86169
86170 namespace Symfony\Component\Process\Tests;
86171
86172 use Symfony\Component\Process\PhpExecutableFinder;
86173
86174 /**
86175 * @author Robert Schönthal <seroscho@googlemail.com>
86176 */
86177 class PhpExecutableFinderTest extends \PHPUnit_Framework_TestCase
86178 {
86179 /**
86180 * tests find() with the env var PHP_PATH
86181 */
86182 public function testFindWithPhpPath()
86183 {
86184 if (defined('PHP_BINARY')) {
86185 $this->markTestSkipped('The PHP binary is easily available as of PHP 5.4');
86186 }
86187
86188 $f = new PhpExecutableFinder();
86189
86190 $current = $f->find();
86191
86192 //not executable PHP_PATH
86193 putenv('PHP_PATH=/not/executable/php');
86194 $this->assertFalse($f->find(), '::find() returns false for not executable php');
86195
86196 //executable PHP_PATH
86197 putenv('PHP_PATH='.$current);
86198 $this->assertEquals($f->find(), $current, '::find() returns the executable php');
86199 }
86200
86201 /**
86202 * tests find() with default executable
86203 */
86204 public function testFindWithSuffix()
86205 {
86206 if (defined('PHP_BINARY')) {
86207 $this->markTestSkipped('The PHP binary is easily available as of PHP 5.4');
86208 }
86209
86210 putenv('PHP_PATH=');
86211 putenv('PHP_PEAR_PHP_BIN=');
86212 $f = new PhpExecutableFinder();
86213
86214 $current = $f->find();
86215
86216 //TODO maybe php executable is custom or even windows
86217 if (defined('PHP_WINDOWS_VERSION_BUILD')) {
86218 $this->assertTrue(is_executable($current));
86219 $this->assertTrue((bool) preg_match('/'.addSlashes(DIRECTORY_SEPARATOR).'php\.(exe|bat|cmd|com)$/i', $current), '::find() returns the executable php with suffixes');
86220 }
86221 }
86222 }
86223 <?php
86224
86225 /*
86226 * This file is part of the Symfony package.
86227 *
86228 * (c) Fabien Potencier <fabien@symfony.com>
86229 *
86230 * For the full copyright and license information, please view the LICENSE
86231 * file that was distributed with this source code.
86232 */
86233
86234 namespace Symfony\Component\Process\Tests;
86235
86236 use Symfony\Component\Process\Process;
86237
86238 class SimpleProcessTest extends AbstractProcessTest
86239 {
86240 private $enabledSigchild = false;
86241
86242 public function setUp()
86243 {
86244 ob_start();
86245 phpinfo(INFO_GENERAL);
86246
86247 $this->enabledSigchild = false !== strpos(ob_get_clean(), '--enable-sigchild');
86248 }
86249
86250 public function testGetExitCode()
86251 {
86252 $this->skipIfPHPSigchild();
86253 parent::testGetExitCode();
86254 }
86255
86256 public function testExitCodeCommandFailed()
86257 {
86258 $this->skipIfPHPSigchild();
86259 parent::testExitCodeCommandFailed();
86260 }
86261
86262 public function testProcessIsSignaledIfStopped()
86263 {
86264 $this->skipIfPHPSigchild();
86265 parent::testProcessIsSignaledIfStopped();
86266 }
86267
86268 public function testProcessWithTermSignal()
86269 {
86270 $this->skipIfPHPSigchild();
86271 parent::testProcessWithTermSignal();
86272 }
86273
86274 public function testProcessIsNotSignaled()
86275 {
86276 $this->skipIfPHPSigchild();
86277 parent::testProcessIsNotSignaled();
86278 }
86279
86280 public function testProcessWithoutTermSignal()
86281 {
86282 $this->skipIfPHPSigchild();
86283 parent::testProcessWithoutTermSignal();
86284 }
86285
86286 public function testExitCodeText()
86287 {
86288 $this->skipIfPHPSigchild();
86289 parent::testExitCodeText();
86290 }
86291
86292 public function testIsSuccessful()
86293 {
86294 $this->skipIfPHPSigchild();
86295 parent::testIsSuccessful();
86296 }
86297
86298 public function testIsNotSuccessful()
86299 {
86300 $this->skipIfPHPSigchild();
86301 parent::testIsNotSuccessful();
86302 }
86303
86304 public function testGetPid()
86305 {
86306 $this->skipIfPHPSigchild();
86307 parent::testGetPid();
86308 }
86309
86310 public function testGetPidIsNullBeforeStart()
86311 {
86312 $this->skipIfPHPSigchild();
86313 parent::testGetPidIsNullBeforeStart();
86314 }
86315
86316 public function testGetPidIsNullAfterRun()
86317 {
86318 $this->skipIfPHPSigchild();
86319 parent::testGetPidIsNullAfterRun();
86320 }
86321
86322 public function testSignal()
86323 {
86324 $this->skipIfPHPSigchild();
86325 parent::testSignal();
86326 }
86327
86328 /**
86329 * @expectedException Symfony\Component\Process\Exception\LogicException
86330 */
86331 public function testSignalProcessNotRunning()
86332 {
86333 $this->skipIfPHPSigchild();
86334 parent::testSignalProcessNotRunning();
86335 }
86336
86337 /**
86338 * @expectedException Symfony\Component\Process\Exception\RuntimeException
86339 */
86340 public function testSignalWithWrongIntSignal()
86341 {
86342 $this->skipIfPHPSigchild();
86343 parent::testSignalWithWrongIntSignal();
86344 }
86345
86346 /**
86347 * @expectedException Symfony\Component\Process\Exception\RuntimeException
86348 */
86349 public function testSignalWithWrongNonIntSignal()
86350 {
86351 $this->skipIfPHPSigchild();
86352 parent::testSignalWithWrongNonIntSignal();
86353 }
86354
86355 /**
86356 * {@inheritdoc}
86357 */
86358 protected function getProcess($commandline, $cwd = null, array $env = null, $stdin = null, $timeout = 60, array $options = array())
86359 {
86360 return new Process($commandline, $cwd, $env, $stdin, $timeout, $options);
86361 }
86362
86363 private function skipIfPHPSigchild()
86364 {
86365 if ($this->enabledSigchild) {
86366 $this->markTestSkipped('Your PHP has been compiled with --enable-sigchild, this test can not be executed');
86367 }
86368 }
86369 }
86370 <?php
86371
86372 /*
86373 * This file is part of the Symfony package.
86374 *
86375 * (c) Fabien Potencier <fabien@symfony.com>
86376 *
86377 * For the full copyright and license information, please view the LICENSE
86378 * file that was distributed with this source code.
86379 */
86380
86381 namespace Symfony\Component\Process\Tests;
86382
86383 use Symfony\Component\Process\Exception\ProcessFailedException;
86384
86385 /**
86386 * @author Sebastian Marek <proofek@gmail.com>
86387 */
86388 class ProcessFailedExceptionTest extends \PHPUnit_Framework_TestCase
86389 {
86390 /**
86391 * tests ProcessFailedException throws exception if the process was successful
86392 */
86393 public function testProcessFailedExceptionThrowsException()
86394 {
86395 $process = $this->getMock(
86396 'Symfony\Component\Process\Process',
86397 array('isSuccessful'),
86398 array('php')
86399 );
86400 $process->expects($this->once())
86401 ->method('isSuccessful')
86402 ->will($this->returnValue(true));
86403
86404 $this->setExpectedException(
86405 '\InvalidArgumentException',
86406 'Expected a failed process, but the given process was successful.'
86407 );
86408
86409 new ProcessFailedException($process);
86410 }
86411
86412 /**
86413 * tests ProcessFailedException uses information from process output
86414 * to generate exception message
86415 */
86416 public function testProcessFailedExceptionPopulatesInformationFromProcessOutput()
86417 {
86418 $cmd = 'php';
86419 $exitCode = 1;
86420 $exitText = 'General error';
86421 $output = "Command output";
86422 $errorOutput = "FATAL: Unexpected error";
86423
86424 $process = $this->getMock(
86425 'Symfony\Component\Process\Process',
86426 array('isSuccessful', 'getOutput', 'getErrorOutput', 'getExitCode', 'getExitCodeText'),
86427 array($cmd)
86428 );
86429 $process->expects($this->once())
86430 ->method('isSuccessful')
86431 ->will($this->returnValue(false));
86432 $process->expects($this->once())
86433 ->method('getOutput')
86434 ->will($this->returnValue($output));
86435 $process->expects($this->once())
86436 ->method('getErrorOutput')
86437 ->will($this->returnValue($errorOutput));
86438 $process->expects($this->once())
86439 ->method('getExitCode')
86440 ->will($this->returnValue($exitCode));
86441 $process->expects($this->once())
86442 ->method('getExitCodeText')
86443 ->will($this->returnValue($exitText));
86444
86445 $exception = new ProcessFailedException($process);
86446
86447 $this->assertEquals(
86448 "The command \"$cmd\" failed.\nExit Code: $exitCode($exitText)\n\nOutput:\n================\n{$output}\n\nError Output:\n================\n{$errorOutput}",
86449 $exception->getMessage()
86450 );
86451 }
86452 }
86453 <?php
86454
86455 /*
86456 * This file is part of the Symfony package.
86457 *
86458 * (c) Fabien Potencier <fabien@symfony.com>
86459 *
86460 * For the full copyright and license information, please view the LICENSE
86461 * file that was distributed with this source code.
86462 */
86463
86464 namespace Symfony\Component\Process\Tests;
86465
86466 class SigchildEnabledProcessTest extends AbstractProcessTest
86467 {
86468 /**
86469 * @expectedException \Symfony\Component\Process\Exception\RuntimeException
86470 */
86471 public function testProcessIsSignaledIfStopped()
86472 {
86473 parent::testProcessIsSignaledIfStopped();
86474 }
86475
86476 /**
86477 * @expectedException \Symfony\Component\Process\Exception\RuntimeException
86478 */
86479 public function testProcessWithTermSignal()
86480 {
86481 parent::testProcessWithTermSignal();
86482 }
86483
86484 /**
86485 * @expectedException \Symfony\Component\Process\Exception\RuntimeException
86486 */
86487 public function testProcessIsNotSignaled()
86488 {
86489 parent::testProcessIsNotSignaled();
86490 }
86491
86492 /**
86493 * @expectedException \Symfony\Component\Process\Exception\RuntimeException
86494 */
86495 public function testProcessWithoutTermSignal()
86496 {
86497 parent::testProcessWithoutTermSignal();
86498 }
86499
86500 /**
86501 * @expectedException Symfony\Component\Process\Exception\RuntimeException
86502 */
86503 public function testGetPid()
86504 {
86505 parent::testGetPid();
86506 }
86507
86508 /**
86509 * @expectedException Symfony\Component\Process\Exception\RuntimeException
86510 */
86511 public function testGetPidIsNullBeforeStart()
86512 {
86513 parent::testGetPidIsNullBeforeStart();
86514 }
86515
86516 /**
86517 * @expectedException Symfony\Component\Process\Exception\RuntimeException
86518 */
86519 public function testGetPidIsNullAfterRun()
86520 {
86521 parent::testGetPidIsNullAfterRun();
86522 }
86523
86524 public function testExitCodeText()
86525 {
86526 $process = $this->getProcess('qdfsmfkqsdfmqmsd');
86527 $process->run();
86528
86529 $this->assertInternalType('string', $process->getExitCodeText());
86530 }
86531
86532 /**
86533 * @expectedException Symfony\Component\Process\Exception\RuntimeException
86534 */
86535 public function testSignal()
86536 {
86537 parent::testSignal();
86538 }
86539
86540 /**
86541 * @expectedException Symfony\Component\Process\Exception\RuntimeException
86542 */
86543 public function testProcessWithoutTermSignalIsNotSignaled()
86544 {
86545 parent::testProcessWithoutTermSignalIsNotSignaled();
86546 }
86547
86548 public function testProcessThrowsExceptionWhenExternallySignaled()
86549 {
86550 $this->markTestSkipped('Retrieving Pid is not supported in sigchild environment');
86551 }
86552
86553 public function testExitCodeIsAvailableAfterSignal()
86554 {
86555 $this->markTestSkipped('Signal is not supported in sigchild environment');
86556 }
86557
86558 /**
86559 * {@inheritdoc}
86560 */
86561 protected function getProcess($commandline, $cwd = null, array $env = null, $stdin = null, $timeout = 60, array $options = array())
86562 {
86563 $process = new ProcessInSigchildEnvironment($commandline, $cwd, $env, $stdin, $timeout, $options);
86564 $process->setEnhanceSigchildCompatibility(true);
86565
86566 return $process;
86567 }
86568 }
86569 <?php
86570
86571 /*
86572 * This file is part of the Symfony package.
86573 *
86574 * (c) Fabien Potencier <fabien@symfony.com>
86575 *
86576 * For the full copyright and license information, please view the LICENSE
86577 * file that was distributed with this source code.
86578 */
86579
86580 namespace Symfony\Component\Process\Tests;
86581
86582 use Symfony\Component\Process\Process;
86583 use Symfony\Component\Process\Exception\RuntimeException;
86584
86585 /**
86586 * @author Robert Schönthal <seroscho@googlemail.com>
86587 */
86588 abstract class AbstractProcessTest extends \PHPUnit_Framework_TestCase
86589 {
86590 /**
86591 * @expectedException \Symfony\Component\Process\Exception\InvalidArgumentException
86592 */
86593 public function testNegativeTimeoutFromConstructor()
86594 {
86595 $this->getProcess('', null, null, null, -1);
86596 }
86597
86598 /**
86599 * @expectedException \Symfony\Component\Process\Exception\InvalidArgumentException
86600 */
86601 public function testNegativeTimeoutFromSetter()
86602 {
86603 $p = $this->getProcess('');
86604 $p->setTimeout(-1);
86605 }
86606
86607 public function testNullTimeout()
86608 {
86609 $p = $this->getProcess('');
86610 $p->setTimeout(10);
86611 $p->setTimeout(null);
86612
86613 $this->assertNull($p->getTimeout());
86614 }
86615
86616 public function testStopWithTimeoutIsActuallyWorking()
86617 {
86618 $this->verifyPosixIsEnabled();
86619
86620 // exec is mandatory here since we send a signal to the process
86621 // see https://github.com/symfony/symfony/issues/5030 about prepending
86622 // command with exec
86623 $p = $this->getProcess('exec php '.__DIR__.'/NonStopableProcess.php 3');
86624 $p->start();
86625 usleep(100000);
86626 $start = microtime(true);
86627 $p->stop(1.1, SIGKILL);
86628 while ($p->isRunning()) {
86629 usleep(1000);
86630 }
86631 $duration = microtime(true) - $start;
86632
86633 $this->assertLessThan(1.8, $duration);
86634 }
86635
86636 public function testCallbacksAreExecutedWithStart()
86637 {
86638 $data = '';
86639
86640 $process = $this->getProcess('echo "foo";sleep 1;echo "foo"');
86641 $process->start(function ($type, $buffer) use (&$data) {
86642 $data .= $buffer;
86643 });
86644
86645 $start = microtime(true);
86646 while ($process->isRunning()) {
86647 usleep(10000);
86648 }
86649
86650 $this->assertEquals("foo\nfoo\n", $data);
86651 }
86652
86653 /**
86654 * tests results from sub processes
86655 *
86656 * @dataProvider responsesCodeProvider
86657 */
86658 public function testProcessResponses($expected, $getter, $code)
86659 {
86660 $p = $this->getProcess(sprintf('php -r %s', escapeshellarg($code)));
86661 $p->run();
86662
86663 $this->assertSame($expected, $p->$getter());
86664 }
86665
86666 /**
86667 * tests results from sub processes
86668 *
86669 * @dataProvider pipesCodeProvider
86670 */
86671 public function testProcessPipes($code, $size)
86672 {
86673 if (defined('PHP_WINDOWS_VERSION_BUILD')) {
86674 $this->markTestSkipped('Test hangs on Windows & PHP due to https://bugs.php.net/bug.php?id=60120 and https://bugs.php.net/bug.php?id=51800');
86675 }
86676
86677 $expected = str_repeat(str_repeat('*', 1024), $size) . '!';
86678 $expectedLength = (1024 * $size) + 1;
86679
86680 $p = $this->getProcess(sprintf('php -r %s', escapeshellarg($code)));
86681 $p->setStdin($expected);
86682 $p->run();
86683
86684 $this->assertEquals($expectedLength, strlen($p->getOutput()));
86685 $this->assertEquals($expectedLength, strlen($p->getErrorOutput()));
86686 }
86687
86688 public function chainedCommandsOutputProvider()
86689 {
86690 return array(
86691 array("1\n1\n", ';', '1'),
86692 array("2\n2\n", '&&', '2'),
86693 );
86694 }
86695
86696 /**
86697 *
86698 * @dataProvider chainedCommandsOutputProvider
86699 */
86700 public function testChainedCommandsOutput($expected, $operator, $input)
86701 {
86702 if (defined('PHP_WINDOWS_VERSION_BUILD')) {
86703 $this->markTestSkipped('Does it work on windows ?');
86704 }
86705
86706 $process = $this->getProcess(sprintf('echo %s %s echo %s', $input, $operator, $input));
86707 $process->run();
86708 $this->assertEquals($expected, $process->getOutput());
86709 }
86710
86711 public function testCallbackIsExecutedForOutput()
86712 {
86713 $p = $this->getProcess(sprintf('php -r %s', escapeshellarg('echo \'foo\';')));
86714
86715 $called = false;
86716 $p->run(function ($type, $buffer) use (&$called) {
86717 $called = $buffer === 'foo';
86718 });
86719
86720 $this->assertTrue($called, 'The callback should be executed with the output');
86721 }
86722
86723 public function testGetErrorOutput()
86724 {
86725 $p = new Process(sprintf('php -r %s', escapeshellarg('$n = 0; while ($n < 3) { file_put_contents(\'php://stderr\', \'ERROR\'); $n++; }')));
86726
86727 $p->run();
86728 $this->assertEquals(3, preg_match_all('/ERROR/', $p->getErrorOutput(), $matches));
86729 }
86730
86731 public function testGetIncrementalErrorOutput()
86732 {
86733 $p = new Process(sprintf('php -r %s', escapeshellarg('$n = 0; while ($n < 3) { usleep(50000); file_put_contents(\'php://stderr\', \'ERROR\'); $n++; }')));
86734
86735 $p->start();
86736 while ($p->isRunning()) {
86737 $this->assertLessThanOrEqual(1, preg_match_all('/ERROR/', $p->getIncrementalErrorOutput(), $matches));
86738 usleep(20000);
86739 }
86740 }
86741
86742 public function testGetOutput()
86743 {
86744 $p = new Process(sprintf('php -r %s', escapeshellarg('$n=0;while ($n<3) {echo \' foo \';$n++;}')));
86745
86746 $p->run();
86747 $this->assertEquals(3, preg_match_all('/foo/', $p->getOutput(), $matches));
86748 }
86749
86750 public function testGetIncrementalOutput()
86751 {
86752 $p = new Process(sprintf('php -r %s', escapeshellarg('$n=0;while ($n<3) { echo \' foo \'; usleep(50000); $n++; }')));
86753
86754 $p->start();
86755 while ($p->isRunning()) {
86756 $this->assertLessThanOrEqual(1, preg_match_all('/foo/', $p->getIncrementalOutput(), $matches));
86757 usleep(20000);
86758 }
86759 }
86760
86761 public function testExitCodeCommandFailed()
86762 {
86763 if (defined('PHP_WINDOWS_VERSION_BUILD')) {
86764 $this->markTestSkipped('Windows does not support POSIX exit code');
86765 }
86766
86767 // such command run in bash return an exitcode 127
86768 $process = $this->getProcess('nonexistingcommandIhopeneversomeonewouldnameacommandlikethis');
86769 $process->run();
86770
86771 $this->assertGreaterThan(0, $process->getExitCode());
86772 }
86773
86774 public function testTTYCommand()
86775 {
86776 if (defined('PHP_WINDOWS_VERSION_BUILD')) {
86777 $this->markTestSkipped('Windows does have /dev/tty support');
86778 }
86779
86780 $process = $this->getProcess('echo "foo" >> /dev/null');
86781 $process->setTTY(true);
86782 $process->run();
86783
86784 $this->assertSame(Process::STATUS_TERMINATED, $process->getStatus());
86785 }
86786
86787 public function testExitCodeText()
86788 {
86789 $process = $this->getProcess('');
86790 $r = new \ReflectionObject($process);
86791 $p = $r->getProperty('exitcode');
86792 $p->setAccessible(true);
86793
86794 $p->setValue($process, 2);
86795 $this->assertEquals('Misuse of shell builtins', $process->getExitCodeText());
86796 }
86797
86798 public function testStartIsNonBlocking()
86799 {
86800 $process = $this->getProcess('php -r "sleep(4);"');
86801 $start = microtime(true);
86802 $process->start();
86803 $end = microtime(true);
86804 $this->assertLessThan(1 , $end-$start);
86805 }
86806
86807 public function testUpdateStatus()
86808 {
86809 $process = $this->getProcess('php -h');
86810 $process->run();
86811 $this->assertTrue(strlen($process->getOutput()) > 0);
86812 }
86813
86814 public function testGetExitCodeIsNullOnStart()
86815 {
86816 $process = $this->getProcess('php -r "usleep(200000);"');
86817 $this->assertNull($process->getExitCode());
86818 $process->start();
86819 $this->assertNull($process->getExitCode());
86820 $process->wait();
86821 $this->assertEquals(0, $process->getExitCode());
86822 }
86823
86824 public function testGetExitCodeIsNullOnWhenStartingAgain()
86825 {
86826 $process = $this->getProcess('php -r "usleep(200000);"');
86827 $process->run();
86828 $this->assertEquals(0, $process->getExitCode());
86829 $process->start();
86830 $this->assertNull($process->getExitCode());
86831 $process->wait();
86832 $this->assertEquals(0, $process->getExitCode());
86833 }
86834
86835 public function testGetExitCode()
86836 {
86837 $process = $this->getProcess('php -m');
86838 $process->run();
86839 $this->assertEquals(0, $process->getExitCode());
86840 }
86841
86842 public function testStatus()
86843 {
86844 $process = $this->getProcess('php -r "usleep(500000);"');
86845 $this->assertFalse($process->isRunning());
86846 $this->assertFalse($process->isStarted());
86847 $this->assertFalse($process->isTerminated());
86848 $this->assertSame(Process::STATUS_READY, $process->getStatus());
86849 $process->start();
86850 $this->assertTrue($process->isRunning());
86851 $this->assertTrue($process->isStarted());
86852 $this->assertFalse($process->isTerminated());
86853 $this->assertSame(Process::STATUS_STARTED, $process->getStatus());
86854 $process->wait();
86855 $this->assertFalse($process->isRunning());
86856 $this->assertTrue($process->isStarted());
86857 $this->assertTrue($process->isTerminated());
86858 $this->assertSame(Process::STATUS_TERMINATED, $process->getStatus());
86859 }
86860
86861 public function testStop()
86862 {
86863 $process = $this->getProcess('php -r "sleep(4);"');
86864 $process->start();
86865 $this->assertTrue($process->isRunning());
86866 $process->stop();
86867 $this->assertFalse($process->isRunning());
86868 }
86869
86870 public function testIsSuccessful()
86871 {
86872 $process = $this->getProcess('php -m');
86873 $process->run();
86874 $this->assertTrue($process->isSuccessful());
86875 }
86876
86877 public function testIsSuccessfulOnlyAfterTerminated()
86878 {
86879 $process = $this->getProcess('sleep 1');
86880 $process->start();
86881 while ($process->isRunning()) {
86882 $this->assertFalse($process->isSuccessful());
86883 usleep(300000);
86884 }
86885
86886 $this->assertTrue($process->isSuccessful());
86887 }
86888
86889 public function testIsNotSuccessful()
86890 {
86891 $process = $this->getProcess('php -r "sleep(4);"');
86892 $process->start();
86893 $this->assertTrue($process->isRunning());
86894 $process->stop();
86895 $this->assertFalse($process->isSuccessful());
86896 }
86897
86898 public function testProcessIsNotSignaled()
86899 {
86900 if (defined('PHP_WINDOWS_VERSION_BUILD')) {
86901 $this->markTestSkipped('Windows does not support POSIX signals');
86902 }
86903
86904 $process = $this->getProcess('php -m');
86905 $process->run();
86906 $this->assertFalse($process->hasBeenSignaled());
86907 }
86908
86909 public function testProcessWithoutTermSignalIsNotSignaled()
86910 {
86911 if (defined('PHP_WINDOWS_VERSION_BUILD')) {
86912 $this->markTestSkipped('Windows does not support POSIX signals');
86913 }
86914
86915 $process = $this->getProcess('php -m');
86916 $process->run();
86917 $this->assertFalse($process->hasBeenSignaled());
86918 }
86919
86920 public function testProcessWithoutTermSignal()
86921 {
86922 if (defined('PHP_WINDOWS_VERSION_BUILD')) {
86923 $this->markTestSkipped('Windows does not support POSIX signals');
86924 }
86925
86926 $process = $this->getProcess('php -m');
86927 $process->run();
86928 $this->assertEquals(0, $process->getTermSignal());
86929 }
86930
86931 public function testProcessIsSignaledIfStopped()
86932 {
86933 if (defined('PHP_WINDOWS_VERSION_BUILD')) {
86934 $this->markTestSkipped('Windows does not support POSIX signals');
86935 }
86936
86937 $process = $this->getProcess('php -r "sleep(4);"');
86938 $process->start();
86939 $process->stop();
86940 $this->assertTrue($process->hasBeenSignaled());
86941 }
86942
86943 public function testProcessWithTermSignal()
86944 {
86945 if (defined('PHP_WINDOWS_VERSION_BUILD')) {
86946 $this->markTestSkipped('Windows does not support POSIX signals');
86947 }
86948
86949 // SIGTERM is only defined if pcntl extension is present
86950 $termSignal = defined('SIGTERM') ? SIGTERM : 15;
86951
86952 $process = $this->getProcess('php -r "sleep(4);"');
86953 $process->start();
86954 $process->stop();
86955
86956 $this->assertEquals($termSignal, $process->getTermSignal());
86957 }
86958
86959 public function testProcessThrowsExceptionWhenExternallySignaled()
86960 {
86961 if (defined('PHP_WINDOWS_VERSION_BUILD')) {
86962 $this->markTestSkipped('Windows does not support POSIX signals');
86963 }
86964
86965 if (!function_exists('posix_kill')) {
86966 $this->markTestSkipped('posix_kill is required for this test');
86967 }
86968
86969 $termSignal = defined('SIGKILL') ? SIGKILL : 9;
86970
86971 $process = $this->getProcess('exec php -r "while (true) {}"');
86972 $process->start();
86973 posix_kill($process->getPid(), $termSignal);
86974
86975 $this->setExpectedException('Symfony\Component\Process\Exception\RuntimeException', 'The process has been signaled with signal "9".');
86976 $process->wait();
86977 }
86978
86979 public function testRestart()
86980 {
86981 $process1 = $this->getProcess('php -r "echo getmypid();"');
86982 $process1->run();
86983 $process2 = $process1->restart();
86984
86985 usleep(300000); // wait for output
86986
86987 // Ensure that both processed finished and the output is numeric
86988 $this->assertFalse($process1->isRunning());
86989 $this->assertFalse($process2->isRunning());
86990 $this->assertTrue(is_numeric($process1->getOutput()));
86991 $this->assertTrue(is_numeric($process2->getOutput()));
86992
86993 // Ensure that restart returned a new process by check that the output is different
86994 $this->assertNotEquals($process1->getOutput(), $process2->getOutput());
86995 }
86996
86997 public function testPhpDeadlock()
86998 {
86999 $this->markTestSkipped('Can course php to hang');
87000
87001 // Sleep doesn't work as it will allow the process to handle signals and close
87002 // file handles from the other end.
87003 $process = $this->getProcess('php -r "while (true) {}"');
87004 $process->start();
87005
87006 // PHP will deadlock when it tries to cleanup $process
87007 }
87008
87009 public function testRunProcessWithTimeout()
87010 {
87011 $timeout = 0.5;
87012 $process = $this->getProcess('sleep 3');
87013 $process->setTimeout($timeout);
87014 $start = microtime(true);
87015 try {
87016 $process->run();
87017 $this->fail('A RuntimeException should have been raised');
87018 } catch (RuntimeException $e) {
87019
87020 }
87021 $duration = microtime(true) - $start;
87022
87023 $this->assertLessThan($timeout + Process::TIMEOUT_PRECISION, $duration);
87024 }
87025
87026 public function testCheckTimeoutOnStartedProcess()
87027 {
87028 $timeout = 0.5;
87029 $precision = 100000;
87030 $process = $this->getProcess('sleep 3');
87031 $process->setTimeout($timeout);
87032 $start = microtime(true);
87033
87034 $process->start();
87035
87036 try {
87037 while ($process->isRunning()) {
87038 $process->checkTimeout();
87039 usleep($precision);
87040 }
87041 $this->fail('A RuntimeException should have been raised');
87042 } catch (RuntimeException $e) {
87043
87044 }
87045 $duration = microtime(true) - $start;
87046
87047 $this->assertLessThan($timeout + $precision, $duration);
87048 $this->assertFalse($process->isSuccessful());
87049 }
87050
87051 public function testGetPid()
87052 {
87053 $process = $this->getProcess('php -r "sleep(1);"');
87054 $process->start();
87055 $this->assertGreaterThan(0, $process->getPid());
87056 $process->stop();
87057 }
87058
87059 public function testGetPidIsNullBeforeStart()
87060 {
87061 $process = $this->getProcess('php -r "sleep(1);"');
87062 $this->assertNull($process->getPid());
87063 }
87064
87065 public function testGetPidIsNullAfterRun()
87066 {
87067 $process = $this->getProcess('php -m');
87068 $process->run();
87069 $this->assertNull($process->getPid());
87070 }
87071
87072 public function testSignal()
87073 {
87074 $this->verifyPosixIsEnabled();
87075
87076 $process = $this->getProcess('exec php -f ' . __DIR__ . '/SignalListener.php');
87077 $process->start();
87078 usleep(500000);
87079 $process->signal(SIGUSR1);
87080
87081 while ($process->isRunning() && false === strpos($process->getoutput(), 'Caught SIGUSR1')) {
87082 usleep(10000);
87083 }
87084
87085 $this->assertEquals('Caught SIGUSR1', $process->getOutput());
87086 }
87087
87088 public function testExitCodeIsAvailableAfterSignal()
87089 {
87090 $this->verifyPosixIsEnabled();
87091
87092 $process = $this->getProcess('sleep 4');
87093 $process->start();
87094 $process->signal(SIGKILL);
87095
87096 while ($process->isRunning()) {
87097 usleep(10000);
87098 }
87099
87100 $this->assertFalse($process->isRunning());
87101 $this->assertTrue($process->hasBeenSignaled());
87102 $this->assertFalse($process->isSuccessful());
87103 $this->assertEquals(137, $process->getExitCode());
87104 }
87105
87106 /**
87107 * @expectedException Symfony\Component\Process\Exception\LogicException
87108 */
87109 public function testSignalProcessNotRunning()
87110 {
87111 $this->verifyPosixIsEnabled();
87112 $process = $this->getProcess('php -m');
87113 $process->signal(SIGHUP);
87114 }
87115
87116 private function verifyPosixIsEnabled()
87117 {
87118 if (defined('PHP_WINDOWS_VERSION_BUILD')) {
87119 $this->markTestSkipped('POSIX signals do not work on windows');
87120 }
87121 if (!defined('SIGUSR1')) {
87122 $this->markTestSkipped('The pcntl extension is not enabled');
87123 }
87124 }
87125
87126 /**
87127 * @expectedException Symfony\Component\Process\Exception\RuntimeException
87128 */
87129 public function testSignalWithWrongIntSignal()
87130 {
87131 if (defined('PHP_WINDOWS_VERSION_BUILD')) {
87132 $this->markTestSkipped('POSIX signals do not work on windows');
87133 }
87134
87135 $process = $this->getProcess('php -r "sleep(3);"');
87136 $process->start();
87137 $process->signal(-4);
87138 }
87139
87140 /**
87141 * @expectedException Symfony\Component\Process\Exception\RuntimeException
87142 */
87143 public function testSignalWithWrongNonIntSignal()
87144 {
87145 if (defined('PHP_WINDOWS_VERSION_BUILD')) {
87146 $this->markTestSkipped('POSIX signals do not work on windows');
87147 }
87148
87149 $process = $this->getProcess('php -r "sleep(3);"');
87150 $process->start();
87151 $process->signal('Céphalopodes');
87152 }
87153
87154 public function responsesCodeProvider()
87155 {
87156 return array(
87157 //expected output / getter / code to execute
87158 //array(1,'getExitCode','exit(1);'),
87159 //array(true,'isSuccessful','exit();'),
87160 array('output', 'getOutput', 'echo \'output\';'),
87161 );
87162 }
87163
87164 public function pipesCodeProvider()
87165 {
87166 $variations = array(
87167 'fwrite(STDOUT, $in = file_get_contents(\'php://stdin\')); fwrite(STDERR, $in);',
87168 'include \''.__DIR__.'/ProcessTestHelper.php\';',
87169 );
87170
87171 $codes = array();
87172 foreach (array(1, 16, 64, 1024, 4096) as $size) {
87173 foreach ($variations as $code) {
87174 $codes[] = array($code, $size);
87175 }
87176 }
87177
87178 return $codes;
87179 }
87180
87181 /**
87182 * provides default method names for simple getter/setter
87183 */
87184 public function methodProvider()
87185 {
87186 $defaults = array(
87187 array('CommandLine'),
87188 array('Timeout'),
87189 array('WorkingDirectory'),
87190 array('Env'),
87191 array('Stdin'),
87192 array('Options')
87193 );
87194
87195 return $defaults;
87196 }
87197
87198 /**
87199 * @param string $commandline
87200 * @param null $cwd
87201 * @param array $env
87202 * @param null $stdin
87203 * @param integer $timeout
87204 * @param array $options
87205 *
87206 * @return Process
87207 */
87208 abstract protected function getProcess($commandline, $cwd = null, array $env = null, $stdin = null, $timeout = 60, array $options = array());
87209 }
87210 <?php
87211
87212 /*
87213 * This file is part of the Symfony package.
87214 *
87215 * (c) Fabien Potencier <fabien@symfony.com>
87216 *
87217 * For the full copyright and license information, please view the LICENSE
87218 * file that was distributed with this source code.
87219 */
87220
87221 namespace Symfony\Component\Process\Tests;
87222
87223 use Symfony\Component\Process\ProcessBuilder;
87224
87225 class ProcessBuilderTest extends \PHPUnit_Framework_TestCase
87226 {
87227 public function testInheritEnvironmentVars()
87228 {
87229 $snapshot = $_ENV;
87230 $_ENV = $expected = array('foo' => 'bar');
87231
87232 $pb = new ProcessBuilder();
87233 $pb->add('foo')->inheritEnvironmentVariables();
87234 $proc = $pb->getProcess();
87235
87236 $this->assertNull($proc->getEnv(), '->inheritEnvironmentVariables() copies $_ENV');
87237
87238 $_ENV = $snapshot;
87239 }
87240
87241 public function testProcessShouldInheritAndOverrideEnvironmentVars()
87242 {
87243 $snapshot = $_ENV;
87244 $_ENV = array('foo' => 'bar', 'bar' => 'baz');
87245 $expected = array('foo' => 'foo', 'bar' => 'baz');
87246
87247 $pb = new ProcessBuilder();
87248 $pb->add('foo')->inheritEnvironmentVariables()
87249 ->setEnv('foo', 'foo');
87250 $proc = $pb->getProcess();
87251
87252 $this->assertEquals($expected, $proc->getEnv(), '->inheritEnvironmentVariables() copies $_ENV');
87253
87254 $_ENV = $snapshot;
87255 }
87256
87257 public function testProcessBuilderShouldNotPassEnvArrays()
87258 {
87259 $snapshot = $_ENV;
87260 $_ENV = array('a' => array('b', 'c'), 'd' => 'e', 'f' => 'g');
87261 $expected = array('d' => 'e', 'f' => 'g');
87262
87263 $pb = new ProcessBuilder();
87264 $pb->add('a')->inheritEnvironmentVariables()
87265 ->setEnv('d', 'e');
87266 $proc = $pb->getProcess();
87267
87268 $this->assertEquals($expected, $proc->getEnv(), '->inheritEnvironmentVariables() removes array values from $_ENV');
87269
87270 $_ENV = $snapshot;
87271 }
87272
87273 public function testInheritEnvironmentVarsByDefault()
87274 {
87275 $pb = new ProcessBuilder();
87276 $proc = $pb->add('foo')->getProcess();
87277
87278 $this->assertNull($proc->getEnv());
87279 }
87280
87281 public function testNotReplaceExplicitlySetVars()
87282 {
87283 $snapshot = $_ENV;
87284 $_ENV = array('foo' => 'bar');
87285 $expected = array('foo' => 'baz');
87286
87287 $pb = new ProcessBuilder();
87288 $pb
87289 ->setEnv('foo', 'baz')
87290 ->inheritEnvironmentVariables()
87291 ->add('foo')
87292 ;
87293 $proc = $pb->getProcess();
87294
87295 $this->assertEquals($expected, $proc->getEnv(), '->inheritEnvironmentVariables() copies $_ENV');
87296
87297 $_ENV = $snapshot;
87298 }
87299
87300 /**
87301 * @expectedException \Symfony\Component\Process\Exception\InvalidArgumentException
87302 */
87303 public function testNegativeTimeoutFromSetter()
87304 {
87305 $pb = new ProcessBuilder();
87306 $pb->setTimeout(-1);
87307 }
87308
87309 public function testNullTimeout()
87310 {
87311 $pb = new ProcessBuilder();
87312 $pb->setTimeout(10);
87313 $pb->setTimeout(null);
87314
87315 $r = new \ReflectionObject($pb);
87316 $p = $r->getProperty('timeout');
87317 $p->setAccessible(true);
87318
87319 $this->assertNull($p->getValue($pb));
87320 }
87321
87322 public function testShouldSetArguments()
87323 {
87324 $pb = new ProcessBuilder(array('initial'));
87325 $pb->setArguments(array('second'));
87326
87327 $proc = $pb->getProcess();
87328
87329 $this->assertContains("second", $proc->getCommandLine());
87330 }
87331
87332 public function testPrefixIsPrependedToAllGeneratedProcess()
87333 {
87334 $pb = new ProcessBuilder();
87335 $pb->setPrefix('/usr/bin/php');
87336
87337 $proc = $pb->setArguments(array('-v'))->getProcess();
87338 if (defined('PHP_WINDOWS_VERSION_BUILD')) {
87339 $this->assertEquals('"/usr/bin/php" "-v"', $proc->getCommandLine());
87340 } else {
87341 $this->assertEquals("'/usr/bin/php' '-v'", $proc->getCommandLine());
87342 }
87343
87344 $proc = $pb->setArguments(array('-i'))->getProcess();
87345 if (defined('PHP_WINDOWS_VERSION_BUILD')) {
87346 $this->assertEquals('"/usr/bin/php" "-i"', $proc->getCommandLine());
87347 } else {
87348 $this->assertEquals("'/usr/bin/php' '-i'", $proc->getCommandLine());
87349 }
87350 }
87351
87352 public function testShouldEscapeArguments()
87353 {
87354 $pb = new ProcessBuilder(array('%path%', 'foo " bar'));
87355 $proc = $pb->getProcess();
87356
87357 if (defined('PHP_WINDOWS_VERSION_BUILD')) {
87358 $this->assertSame('^%"path"^% "foo "\\"" bar"', $proc->getCommandLine());
87359 } else {
87360 $this->assertSame("'%path%' 'foo \" bar'", $proc->getCommandLine());
87361 }
87362 }
87363
87364 public function testShouldEscapeArgumentsAndPrefix()
87365 {
87366 $pb = new ProcessBuilder(array('arg'));
87367 $pb->setPrefix('%prefix%');
87368 $proc = $pb->getProcess();
87369
87370 if (defined('PHP_WINDOWS_VERSION_BUILD')) {
87371 $this->assertSame('^%"prefix"^% "arg"', $proc->getCommandLine());
87372 } else {
87373 $this->assertSame("'%prefix%' 'arg'", $proc->getCommandLine());
87374 }
87375 }
87376
87377 /**
87378 * @expectedException \Symfony\Component\Process\Exception\LogicException
87379 */
87380 public function testShouldThrowALogicExceptionIfNoPrefixAndNoArgument()
87381 {
87382 ProcessBuilder::create()->getProcess();
87383 }
87384
87385 public function testShouldNotThrowALogicExceptionIfNoArgument()
87386 {
87387 $process = ProcessBuilder::create()
87388 ->setPrefix('/usr/bin/php')
87389 ->getProcess();
87390
87391 if (defined('PHP_WINDOWS_VERSION_BUILD')) {
87392 $this->assertEquals('"/usr/bin/php"', $process->getCommandLine());
87393 } else {
87394 $this->assertEquals("'/usr/bin/php'", $process->getCommandLine());
87395 }
87396 }
87397
87398 public function testShouldNotThrowALogicExceptionIfNoPrefix()
87399 {
87400 $process = ProcessBuilder::create(array('/usr/bin/php'))
87401 ->getProcess();
87402
87403 if (defined('PHP_WINDOWS_VERSION_BUILD')) {
87404 $this->assertEquals('"/usr/bin/php"', $process->getCommandLine());
87405 } else {
87406 $this->assertEquals("'/usr/bin/php'", $process->getCommandLine());
87407 }
87408 }
87409 }
87410 <?php
87411
87412 // required for signal handling
87413 declare(ticks = 1);
87414
87415 pcntl_signal(SIGUSR1, function(){echo "Caught SIGUSR1"; exit;});
87416
87417 $n=0;
87418
87419 // ticks require activity to work - sleep(4); does not work
87420 while ($n < 400) {
87421 usleep(10000);
87422 $n++;
87423 }
87424
87425 return;
87426 <?php
87427
87428 /*
87429 * This file is part of the Symfony package.
87430 *
87431 * (c) Fabien Potencier <fabien@symfony.com>
87432 *
87433 * For the full copyright and license information, please view the LICENSE
87434 * file that was distributed with this source code.
87435 */
87436
87437 namespace Symfony\Component\Process\Tests;
87438
87439 use Symfony\Component\Process\Process;
87440
87441 class ProcessInSigchildEnvironment extends Process
87442 {
87443 protected function isSigchildEnabled()
87444 {
87445 return true;
87446 }
87447 }
87448 <?php
87449
87450 /**
87451 * Runs a PHP script that can be stopped only with a SIGKILL (9) signal for 3 seconds
87452 *
87453 * @args duration Run this script with a custom duration
87454 *
87455 * @example `php NonStopableProcess.php 42` will run the script for 42 seconds
87456 */
87457
87458 function handleSignal($signal)
87459 {
87460 switch ($signal) {
87461 case SIGTERM:
87462 $name = 'SIGTERM';
87463 break;
87464 case SIGINT:
87465 $name = 'SIGINT';
87466 break;
87467 default:
87468 $name = $signal . ' (unknown)';
87469 break;
87470 }
87471
87472 echo "received signal $name\n";
87473 }
87474
87475 declare(ticks=1);
87476 pcntl_signal(SIGTERM, 'handleSignal');
87477 pcntl_signal(SIGINT, 'handleSignal');
87478
87479 $duration = isset($argv[1]) ? (int) $argv[1] : 3;
87480 $start = microtime(true);
87481
87482 while ($duration > (microtime(true) - $start)) {
87483 usleep(1000);
87484 }
87485 <?php
87486
87487 define('ERR_SELECT_FAILED', 1);
87488 define('ERR_TIMEOUT', 2);
87489 define('ERR_READ_FAILED', 3);
87490 define('ERR_WRITE_FAILED', 4);
87491
87492 $read = array(STDIN);
87493 $write = array(STDOUT, STDERR);
87494
87495 stream_set_blocking(STDIN, false);
87496 stream_set_blocking(STDOUT, false);
87497 stream_set_blocking(STDERR, false);
87498
87499 $out = $err = '';
87500 while ($read || $write) {
87501 $r = $read;
87502 $w = $write;
87503 $e = null;
87504 $n = stream_select($r, $w, $e, 5);
87505
87506 if (false === $n) {
87507 die(ERR_SELECT_FAILED);
87508 } elseif ($n < 1) {
87509 die(ERR_TIMEOUT);
87510 }
87511
87512 if (in_array(STDOUT, $w) && strlen($out) > 0) {
87513 $written = fwrite(STDOUT, (binary) $out, 1024);
87514 if (false === $written) {
87515 die(ERR_WRITE_FAILED);
87516 }
87517 $out = (binary) substr($out, $written);
87518 }
87519 if (null === $read && strlen($out) < 1) {
87520 $write = array_diff($write, array(STDOUT));
87521 }
87522
87523 if (in_array(STDERR, $w) && strlen($err) > 0) {
87524 $written = fwrite(STDERR, (binary) $err, 1024);
87525 if (false === $written) {
87526 die(ERR_WRITE_FAILED);
87527 }
87528 $err = (binary) substr($err, $written);
87529 }
87530 if (null === $read && strlen($err) < 1) {
87531 $write = array_diff($write, array(STDERR));
87532 }
87533
87534 if ($r) {
87535 $str = fread(STDIN, 1024);
87536 if (false !== $str) {
87537 $out .= $str;
87538 $err .= $str;
87539 }
87540 if (false === $str || feof(STDIN)) {
87541 $read = null;
87542 if (!feof(STDIN)) {
87543 die(ERR_READ_FAILED);
87544 }
87545 }
87546 }
87547 }
87548 <?php
87549
87550 /*
87551 * This file is part of the Symfony package.
87552 *
87553 * (c) Fabien Potencier <fabien@symfony.com>
87554 *
87555 * For the full copyright and license information, please view the LICENSE
87556 * file that was distributed with this source code.
87557 */
87558
87559 namespace Symfony\Component\Process\Tests;
87560
87561 use Symfony\Component\Process\PhpProcess;
87562
87563 class PhpProcessTest extends \PHPUnit_Framework_TestCase
87564 {
87565 public function testNonBlockingWorks()
87566 {
87567 $expected = 'hello world!';
87568 $process = new PhpProcess(<<<PHP
87569 <?php echo '$expected';
87570 PHP
87571 );
87572 $process->start();
87573 $process->wait();
87574 $this->assertEquals($expected, $process->getOutput());
87575 }
87576 }
87577 <?php
87578
87579 /*
87580 * This file is part of the Symfony package.
87581 *
87582 * (c) Fabien Potencier <fabien@symfony.com>
87583 *
87584 * For the full copyright and license information, please view the LICENSE
87585 * file that was distributed with this source code.
87586 */
87587
87588 namespace Symfony\Component\Process\Tests;
87589
87590 class SigchildDisabledProcessTest extends AbstractProcessTest
87591 {
87592 /**
87593 * @expectedException \Symfony\Component\Process\Exception\RuntimeException
87594 */
87595 public function testGetExitCode()
87596 {
87597 parent::testGetExitCode();
87598 }
87599
87600 /**
87601 * @expectedException \Symfony\Component\Process\Exception\RuntimeException
87602 */
87603 public function testGetExitCodeIsNullOnStart()
87604 {
87605 parent::testGetExitCodeIsNullOnStart();
87606 }
87607
87608 /**
87609 * @expectedException \Symfony\Component\Process\Exception\RuntimeException
87610 */
87611 public function testGetExitCodeIsNullOnWhenStartingAgain()
87612 {
87613 parent::testGetExitCodeIsNullOnWhenStartingAgain();
87614 }
87615
87616 /**
87617 * @expectedException \Symfony\Component\Process\Exception\RuntimeException
87618 */
87619 public function testExitCodeCommandFailed()
87620 {
87621 parent::testExitCodeCommandFailed();
87622 }
87623
87624 /**
87625 * @expectedException \Symfony\Component\Process\Exception\RuntimeException
87626 */
87627 public function testProcessIsSignaledIfStopped()
87628 {
87629 parent::testProcessIsSignaledIfStopped();
87630 }
87631
87632 /**
87633 * @expectedException \Symfony\Component\Process\Exception\RuntimeException
87634 */
87635 public function testProcessWithTermSignal()
87636 {
87637 parent::testProcessWithTermSignal();
87638 }
87639
87640 /**
87641 * @expectedException \Symfony\Component\Process\Exception\RuntimeException
87642 */
87643 public function testProcessIsNotSignaled()
87644 {
87645 parent::testProcessIsNotSignaled();
87646 }
87647
87648 /**
87649 * @expectedException \Symfony\Component\Process\Exception\RuntimeException
87650 */
87651 public function testProcessWithoutTermSignal()
87652 {
87653 parent::testProcessWithoutTermSignal();
87654 }
87655
87656 /**
87657 * @expectedException \Symfony\Component\Process\Exception\RuntimeException
87658 */
87659 public function testCheckTimeoutOnStartedProcess()
87660 {
87661 parent::testCheckTimeoutOnStartedProcess();
87662 }
87663
87664 /**
87665 * @expectedException \Symfony\Component\Process\Exception\RuntimeException
87666 */
87667 public function testGetPid()
87668 {
87669 parent::testGetPid();
87670 }
87671
87672 /**
87673 * @expectedException Symfony\Component\Process\Exception\RuntimeException
87674 */
87675 public function testGetPidIsNullBeforeStart()
87676 {
87677 parent::testGetPidIsNullBeforeStart();
87678 }
87679
87680 /**
87681 * @expectedException Symfony\Component\Process\Exception\RuntimeException
87682 */
87683 public function testGetPidIsNullAfterRun()
87684 {
87685 parent::testGetPidIsNullAfterRun();
87686 }
87687
87688 /**
87689 * @expectedException Symfony\Component\Process\Exception\RuntimeException
87690 */
87691 public function testExitCodeText()
87692 {
87693 $process = $this->getProcess('qdfsmfkqsdfmqmsd');
87694 $process->run();
87695
87696 $process->getExitCodeText();
87697 }
87698
87699 /**
87700 * @expectedException \Symfony\Component\Process\Exception\RuntimeException
87701 */
87702 public function testIsSuccessful()
87703 {
87704 parent::testIsSuccessful();
87705 }
87706
87707 /**
87708 * @expectedException \Symfony\Component\Process\Exception\RuntimeException
87709 */
87710 public function testIsSuccessfulOnlyAfterTerminated()
87711 {
87712 parent::testIsSuccessfulOnlyAfterTerminated();
87713 }
87714
87715 /**
87716 * @expectedException \Symfony\Component\Process\Exception\RuntimeException
87717 */
87718 public function testIsNotSuccessful()
87719 {
87720 parent::testIsNotSuccessful();
87721 }
87722
87723 /**
87724 * @expectedException Symfony\Component\Process\Exception\RuntimeException
87725 */
87726 public function testSignal()
87727 {
87728 parent::testSignal();
87729 }
87730
87731 /**
87732 * @expectedException Symfony\Component\Process\Exception\RuntimeException
87733 */
87734 public function testProcessWithoutTermSignalIsNotSignaled()
87735 {
87736 parent::testProcessWithoutTermSignalIsNotSignaled();
87737 }
87738
87739 public function testStopWithTimeoutIsActuallyWorking()
87740 {
87741 $this->markTestSkipped('Stopping with signal is not supported in sigchild environment');
87742 }
87743
87744 public function testProcessThrowsExceptionWhenExternallySignaled()
87745 {
87746 $this->markTestSkipped('Retrieving Pid is not supported in sigchild environment');
87747 }
87748
87749 public function testExitCodeIsAvailableAfterSignal()
87750 {
87751 $this->markTestSkipped('Signal is not supported in sigchild environment');
87752 }
87753
87754 /**
87755 * {@inheritdoc}
87756 */
87757 protected function getProcess($commandline, $cwd = null, array $env = null, $stdin = null, $timeout = 60, array $options = array())
87758 {
87759 $process = new ProcessInSigchildEnvironment($commandline, $cwd, $env, $stdin, $timeout, $options);
87760 $process->setEnhanceSigchildCompatibility(false);
87761
87762 return $process;
87763 }
87764 }
87765 Process Component
87766 =================
87767
87768 Process executes commands in sub-processes.
87769
87770 In this example, we run a simple directory listing and get the result back:
87771
87772 use Symfony\Component\Process\Process;
87773
87774 $process = new Process('ls -lsa');
87775 $process->setTimeout(3600);
87776 $process->run();
87777 if (!$process->isSuccessful()) {
87778 throw new RuntimeException($process->getErrorOutput());
87779 }
87780
87781 print $process->getOutput();
87782
87783 You can think that this is easy to achieve with plain PHP but it's not especially
87784 if you want to take care of the subtle differences between the different platforms.
87785
87786 And if you want to be able to get some feedback in real-time, just pass an
87787 anonymous function to the ``run()`` method and you will get the output buffer
87788 as it becomes available:
87789
87790 use Symfony\Component\Process\Process;
87791
87792 $process = new Process('ls -lsa');
87793 $process->run(function ($type, $buffer) {
87794 if ('err' === $type) {
87795 echo 'ERR > '.$buffer;
87796 } else {
87797 echo 'OUT > '.$buffer;
87798 }
87799 });
87800
87801 That's great if you want to execute a long running command (like rsync-ing files to a
87802 remote server) and give feedback to the user in real-time.
87803
87804 Resources
87805 ---------
87806
87807 You can run the unit tests with the following command:
87808
87809 $ cd path/to/Symfony/Component/XXX/
87810 $ composer.phar install --dev
87811 $ phpunit
87812 Copyright (c) 2004-2013 Fabien Potencier
87813
87814 Permission is hereby granted, free of charge, to any person obtaining a copy
87815 of this software and associated documentation files (the "Software"), to deal
87816 in the Software without restriction, including without limitation the rights
87817 to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
87818 copies of the Software, and to permit persons to whom the Software is furnished
87819 to do so, subject to the following conditions:
87820
87821 The above copyright notice and this permission notice shall be included in all
87822 copies or substantial portions of the Software.
87823
87824 THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
87825 IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
87826 FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
87827 AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
87828 LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
87829 OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
87830 THE SOFTWARE.
87831 <?php
87832
87833 /*
87834 * This file is part of the Symfony package.
87835 *
87836 * (c) Fabien Potencier <fabien@symfony.com>
87837 *
87838 * For the full copyright and license information, please view the LICENSE
87839 * file that was distributed with this source code.
87840 */
87841
87842 namespace Symfony\Component\Process;
87843
87844 /**
87845 * Generic executable finder.
87846 *
87847 * @author Fabien Potencier <fabien@symfony.com>
87848 * @author Johannes M. Schmitt <schmittjoh@gmail.com>
87849 */
87850 class ExecutableFinder
87851 {
87852 private $suffixes = array('.exe', '.bat', '.cmd', '.com');
87853
87854 /**
87855 * Replaces default suffixes of executable.
87856 *
87857 * @param array $suffixes
87858 */
87859 public function setSuffixes(array $suffixes)
87860 {
87861 $this->suffixes = $suffixes;
87862 }
87863
87864 /**
87865 * Adds new possible suffix to check for executable.
87866 *
87867 * @param string $suffix
87868 */
87869 public function addSuffix($suffix)
87870 {
87871 $this->suffixes[] = $suffix;
87872 }
87873
87874 /**
87875 * Finds an executable by name.
87876 *
87877 * @param string $name The executable name (without the extension)
87878 * @param string $default The default to return if no executable is found
87879 * @param array $extraDirs Additional dirs to check into
87880 *
87881 * @return string The executable path or default value
87882 */
87883 public function find($name, $default = null, array $extraDirs = array())
87884 {
87885 if (ini_get('open_basedir')) {
87886 $searchPath = explode(PATH_SEPARATOR, getenv('open_basedir'));
87887 $dirs = array();
87888 foreach ($searchPath as $path) {
87889 if (is_dir($path)) {
87890 $dirs[] = $path;
87891 } else {
87892 $file = str_replace(dirname($path), '', $path);
87893 if ($file == $name && is_executable($path)) {
87894 return $path;
87895 }
87896 }
87897 }
87898 } else {
87899 $dirs = array_merge(
87900 explode(PATH_SEPARATOR, getenv('PATH') ?: getenv('Path')),
87901 $extraDirs
87902 );
87903 }
87904
87905 $suffixes = array('');
87906 if (defined('PHP_WINDOWS_VERSION_BUILD')) {
87907 $pathExt = getenv('PATHEXT');
87908 $suffixes = $pathExt ? explode(PATH_SEPARATOR, $pathExt) : $this->suffixes;
87909 }
87910 foreach ($suffixes as $suffix) {
87911 foreach ($dirs as $dir) {
87912 if (is_file($file = $dir.DIRECTORY_SEPARATOR.$name.$suffix) && (defined('PHP_WINDOWS_VERSION_BUILD') || is_executable($file))) {
87913 return $file;
87914 }
87915 }
87916 }
87917
87918 return $default;
87919 }
87920 }
87921 <?php
87922
87923 /*
87924 * This file is part of the Symfony package.
87925 *
87926 * (c) Fabien Potencier <fabien@symfony.com>
87927 *
87928 * For the full copyright and license information, please view the LICENSE
87929 * file that was distributed with this source code.
87930 */
87931
87932 namespace Symfony\Component\Process;
87933
87934 use Symfony\Component\Process\Exception\InvalidArgumentException;
87935 use Symfony\Component\Process\Exception\LogicException;
87936
87937 /**
87938 * Process builder.
87939 *
87940 * @author Kris Wallsmith <kris@symfony.com>
87941 */
87942 class ProcessBuilder
87943 {
87944 private $arguments;
87945 private $cwd;
87946 private $env;
87947 private $stdin;
87948 private $timeout;
87949 private $options;
87950 private $inheritEnv;
87951 private $prefix;
87952
87953 public function __construct(array $arguments = array())
87954 {
87955 $this->arguments = $arguments;
87956
87957 $this->timeout = 60;
87958 $this->options = array();
87959 $this->env = array();
87960 $this->inheritEnv = true;
87961 }
87962
87963 public static function create(array $arguments = array())
87964 {
87965 return new static($arguments);
87966 }
87967
87968 /**
87969 * Adds an unescaped argument to the command string.
87970 *
87971 * @param string $argument A command argument
87972 *
87973 * @return ProcessBuilder
87974 */
87975 public function add($argument)
87976 {
87977 $this->arguments[] = $argument;
87978
87979 return $this;
87980 }
87981
87982 /**
87983 * Adds an unescaped prefix to the command string.
87984 *
87985 * The prefix is preserved when reseting arguments.
87986 *
87987 * @param string $prefix A command prefix
87988 *
87989 * @return ProcessBuilder
87990 */
87991 public function setPrefix($prefix)
87992 {
87993 $this->prefix = $prefix;
87994
87995 return $this;
87996 }
87997
87998 /**
87999 * @param array $arguments
88000 *
88001 * @return ProcessBuilder
88002 */
88003 public function setArguments(array $arguments)
88004 {
88005 $this->arguments = $arguments;
88006
88007 return $this;
88008 }
88009
88010 public function setWorkingDirectory($cwd)
88011 {
88012 $this->cwd = $cwd;
88013
88014 return $this;
88015 }
88016
88017 public function inheritEnvironmentVariables($inheritEnv = true)
88018 {
88019 $this->inheritEnv = $inheritEnv;
88020
88021 return $this;
88022 }
88023
88024 public function setEnv($name, $value)
88025 {
88026 $this->env[$name] = $value;
88027
88028 return $this;
88029 }
88030
88031 public function setInput($stdin)
88032 {
88033 $this->stdin = $stdin;
88034
88035 return $this;
88036 }
88037
88038 /**
88039 * Sets the process timeout.
88040 *
88041 * To disable the timeout, set this value to null.
88042 *
88043 * @param float|null
88044 *
88045 * @return ProcessBuilder
88046 *
88047 * @throws InvalidArgumentException
88048 */
88049 public function setTimeout($timeout)
88050 {
88051 if (null === $timeout) {
88052 $this->timeout = null;
88053
88054 return $this;
88055 }
88056
88057 $timeout = (float) $timeout;
88058
88059 if ($timeout < 0) {
88060 throw new InvalidArgumentException('The timeout value must be a valid positive integer or float number.');
88061 }
88062
88063 $this->timeout = $timeout;
88064
88065 return $this;
88066 }
88067
88068 public function setOption($name, $value)
88069 {
88070 $this->options[$name] = $value;
88071
88072 return $this;
88073 }
88074
88075 public function getProcess()
88076 {
88077 if (!$this->prefix && !count($this->arguments)) {
88078 throw new LogicException('You must add() command arguments before calling getProcess().');
88079 }
88080
88081 $options = $this->options;
88082
88083 $arguments = $this->prefix ? array_merge(array($this->prefix), $this->arguments) : $this->arguments;
88084 $script = implode(' ', array_map(array(__NAMESPACE__.'\\ProcessUtils', 'escapeArgument'), $arguments));
88085
88086 if ($this->inheritEnv) {
88087 $env = $this->env ? $this->env + $_ENV : null;
88088 } else {
88089 $env = $this->env;
88090 }
88091
88092 return new Process($script, $this->cwd, $env, $this->stdin, $this->timeout, $options);
88093 }
88094 }
88095 <?php
88096
88097 /*
88098 * This file is part of the Symfony package.
88099 *
88100 * (c) Fabien Potencier <fabien@symfony.com>
88101 *
88102 * For the full copyright and license information, please view the LICENSE
88103 * file that was distributed with this source code.
88104 */
88105
88106 namespace Symfony\Component\Process;
88107
88108 /**
88109 * ProcessUtils is a bunch of utility methods.
88110 *
88111 * This class contains static methods only and is not meant to be instantiated.
88112 *
88113 * @author Martin Hasoň <martin.hason@gmail.com>
88114 */
88115 class ProcessUtils
88116 {
88117 /**
88118 * This class should not be instantiated
88119 */
88120 private function __construct()
88121 {
88122 }
88123
88124 /**
88125 * Escapes a string to be used as a shell argument.
88126 *
88127 * @param string $argument The argument that will be escaped
88128 *
88129 * @return string The escaped argument
88130 */
88131 public static function escapeArgument($argument)
88132 {
88133 //Fix for PHP bug #43784 escapeshellarg removes % from given string
88134 //Fix for PHP bug #49446 escapeshellarg dosn`t work on windows
88135 //@see https://bugs.php.net/bug.php?id=43784
88136 //@see https://bugs.php.net/bug.php?id=49446
88137 if (defined('PHP_WINDOWS_VERSION_BUILD')) {
88138 if ('' === $argument) {
88139 return escapeshellarg($argument);
88140 }
88141
88142 $escapedArgument = '';
88143 foreach (preg_split('/([%"])/i', $argument, -1, PREG_SPLIT_NO_EMPTY | PREG_SPLIT_DELIM_CAPTURE) as $part) {
88144 if ('"' === $part) {
88145 $escapedArgument .= '\\"';
88146 } elseif ('%' === $part) {
88147 $escapedArgument .= '^%';
88148 } else {
88149 $escapedArgument .= escapeshellarg($part);
88150 }
88151 }
88152
88153 return $escapedArgument;
88154 }
88155
88156 return escapeshellarg($argument);
88157 }
88158 }
88159 <?php
88160
88161 /*
88162 * This file is part of the Symfony package.
88163 *
88164 * (c) Fabien Potencier <fabien@symfony.com>
88165 *
88166 * For the full copyright and license information, please view the LICENSE
88167 * file that was distributed with this source code.
88168 */
88169
88170 namespace Symfony\Component\Process\Exception;
88171
88172 /**
88173 * RuntimeException for the Process Component.
88174 *
88175 * @author Johannes M. Schmitt <schmittjoh@gmail.com>
88176 */
88177 class RuntimeException extends \RuntimeException implements ExceptionInterface
88178 {
88179 }
88180 <?php
88181
88182 /*
88183 * This file is part of the Symfony package.
88184 *
88185 * (c) Fabien Potencier <fabien@symfony.com>
88186 *
88187 * For the full copyright and license information, please view the LICENSE
88188 * file that was distributed with this source code.
88189 */
88190
88191 namespace Symfony\Component\Process\Exception;
88192
88193 /**
88194 * InvalidArgumentException for the Process Component.
88195 *
88196 * @author Romain Neutron <imprec@gmail.com>
88197 */
88198 class InvalidArgumentException extends \InvalidArgumentException implements ExceptionInterface
88199 {
88200 }
88201 <?php
88202
88203 /*
88204 * This file is part of the Symfony package.
88205 *
88206 * (c) Fabien Potencier <fabien@symfony.com>
88207 *
88208 * For the full copyright and license information, please view the LICENSE
88209 * file that was distributed with this source code.
88210 */
88211
88212 namespace Symfony\Component\Process\Exception;
88213
88214 /**
88215 * LogicException for the Process Component.
88216 *
88217 * @author Romain Neutron <imprec@gmail.com>
88218 */
88219 class LogicException extends \LogicException implements ExceptionInterface
88220 {
88221 }
88222 <?php
88223
88224 /*
88225 * This file is part of the Symfony package.
88226 *
88227 * (c) Fabien Potencier <fabien@symfony.com>
88228 *
88229 * For the full copyright and license information, please view the LICENSE
88230 * file that was distributed with this source code.
88231 */
88232
88233 namespace Symfony\Component\Process\Exception;
88234
88235 use Symfony\Component\Process\Process;
88236
88237 /**
88238 * Exception for failed processes.
88239 *
88240 * @author Johannes M. Schmitt <schmittjoh@gmail.com>
88241 */
88242 class ProcessFailedException extends RuntimeException
88243 {
88244 private $process;
88245
88246 public function __construct(Process $process)
88247 {
88248 if ($process->isSuccessful()) {
88249 throw new InvalidArgumentException('Expected a failed process, but the given process was successful.');
88250 }
88251
88252 parent::__construct(
88253 sprintf(
88254 'The command "%s" failed.'."\nExit Code: %s(%s)\n\nOutput:\n================\n%s\n\nError Output:\n================\n%s",
88255 $process->getCommandLine(),
88256 $process->getExitCode(),
88257 $process->getExitCodeText(),
88258 $process->getOutput(),
88259 $process->getErrorOutput()
88260 )
88261 );
88262
88263 $this->process = $process;
88264 }
88265
88266 public function getProcess()
88267 {
88268 return $this->process;
88269 }
88270 }
88271 <?php
88272
88273 /*
88274 * This file is part of the Symfony package.
88275 *
88276 * (c) Fabien Potencier <fabien@symfony.com>
88277 *
88278 * For the full copyright and license information, please view the LICENSE
88279 * file that was distributed with this source code.
88280 */
88281
88282 namespace Symfony\Component\Process\Exception;
88283
88284 /**
88285 * Marker Interface for the Process Component.
88286 *
88287 * @author Johannes M. Schmitt <schmittjoh@gmail.com>
88288 */
88289 interface ExceptionInterface
88290 {
88291 }
88292 CHANGELOG
88293 =========
88294
88295 2.3.0
88296 -----
88297
88298 * added ProcessUtils::escapeArgument() to fix the bug in escapeshellarg() function on Windows
88299 * added Process::signal()
88300 * added Process::getPid()
88301 * added support for a TTY mode
88302
88303 2.2.0
88304 -----
88305
88306 * added ProcessBuilder::setArguments() to reset the arguments on a builder
88307 * added a way to retrieve the standard and error output incrementally
88308 * added Process:restart()
88309
88310 2.1.0
88311 -----
88312
88313 * added support for non-blocking processes (start(), wait(), isRunning(), stop())
88314 * enhanced Windows compatibility
88315 * added Process::getExitCodeText() that returns a string representation for
88316 the exit code returned by the process
88317 * added ProcessBuilder
88318 <?php
88319
88320 /*
88321 * This file is part of the Symfony package.
88322 *
88323 * (c) Fabien Potencier <fabien@symfony.com>
88324 *
88325 * For the full copyright and license information, please view the LICENSE
88326 * file that was distributed with this source code.
88327 */
88328
88329 namespace Symfony\Component\Process;
88330
88331 use Symfony\Component\Process\Exception\RuntimeException;
88332
88333 /**
88334 * PhpProcess runs a PHP script in an independent process.
88335 *
88336 * $p = new PhpProcess('<?php echo "foo"; ?>');
88337 * $p->run();
88338 * print $p->getOutput()."\n";
88339 *
88340 * @author Fabien Potencier <fabien@symfony.com>
88341 *
88342 * @api
88343 */
88344 class PhpProcess extends Process
88345 {
88346 private $executableFinder;
88347
88348 /**
88349 * Constructor.
88350 *
88351 * @param string $script The PHP script to run (as a string)
88352 * @param string $cwd The working directory
88353 * @param array $env The environment variables
88354 * @param integer $timeout The timeout in seconds
88355 * @param array $options An array of options for proc_open
88356 *
88357 * @api
88358 */
88359 public function __construct($script, $cwd = null, array $env = array(), $timeout = 60, array $options = array())
88360 {
88361 parent::__construct(null, $cwd, $env, $script, $timeout, $options);
88362
88363 $this->executableFinder = new PhpExecutableFinder();
88364 }
88365
88366 /**
88367 * Sets the path to the PHP binary to use.
88368 *
88369 * @api
88370 */
88371 public function setPhpBinary($php)
88372 {
88373 $this->setCommandLine($php);
88374 }
88375
88376 /**
88377 * {@inheritdoc}
88378 */
88379 public function start($callback = null)
88380 {
88381 if (null === $this->getCommandLine()) {
88382 if (false === $php = $this->executableFinder->find()) {
88383 throw new RuntimeException('Unable to find the PHP executable.');
88384 }
88385 $this->setCommandLine($php);
88386 }
88387
88388 parent::start($callback);
88389 }
88390 }
88391 <?php
88392
88393 /*
88394 * This file is part of the Symfony package.
88395 *
88396 * (c) Fabien Potencier <fabien@symfony.com>
88397 *
88398 * For the full copyright and license information, please view the LICENSE
88399 * file that was distributed with this source code.
88400 */
88401
88402 namespace Symfony\Component\Process;
88403
88404 /**
88405 * An executable finder specifically designed for the PHP executable.
88406 *
88407 * @author Fabien Potencier <fabien@symfony.com>
88408 * @author Johannes M. Schmitt <schmittjoh@gmail.com>
88409 */
88410 class PhpExecutableFinder
88411 {
88412 private $executableFinder;
88413
88414 public function __construct()
88415 {
88416 $this->executableFinder = new ExecutableFinder();
88417 }
88418
88419 /**
88420 * Finds The PHP executable.
88421 *
88422 * @return string|false The PHP executable path or false if it cannot be found
88423 */
88424 public function find()
88425 {
88426 // PHP_BINARY return the current sapi executable
88427 if (defined('PHP_BINARY') && PHP_BINARY && ('cli' === PHP_SAPI) && is_file(PHP_BINARY)) {
88428 return PHP_BINARY;
88429 }
88430
88431 if ($php = getenv('PHP_PATH')) {
88432 if (!is_executable($php)) {
88433 return false;
88434 }
88435
88436 return $php;
88437 }
88438
88439 if ($php = getenv('PHP_PEAR_PHP_BIN')) {
88440 if (is_executable($php)) {
88441 return $php;
88442 }
88443 }
88444
88445 $dirs = array(PHP_BINDIR);
88446 if (defined('PHP_WINDOWS_VERSION_BUILD')) {
88447 $dirs[] = 'C:\xampp\php\\';
88448 }
88449
88450 return $this->executableFinder->find('php', false, $dirs);
88451 }
88452 }
88453 {
88454 "name": "symfony/process",
88455 "type": "library",
88456 "description": "Symfony Process Component",
88457 "keywords": [],
88458 "homepage": "http://symfony.com",
88459 "license": "MIT",
88460 "authors": [
88461 {
88462 "name": "Fabien Potencier",
88463 "email": "fabien@symfony.com"
88464 },
88465 {
88466 "name": "Symfony Community",
88467 "homepage": "http://symfony.com/contributors"
88468 }
88469 ],
88470 "require": {
88471 "php": ">=5.3.3"
88472 },
88473 "autoload": {
88474 "psr-0": { "Symfony\\Component\\Process\\": "" }
88475 },
88476 "target-dir": "Symfony/Component/Process",
88477 "minimum-stability": "dev",
88478 "extra": {
88479 "branch-alias": {
88480 "dev-master": "2.3-dev"
88481 }
88482 }
88483 }
88484 <?xml version="1.0" encoding="UTF-8"?>
88485
88486 <phpunit backupGlobals="false"
88487 backupStaticAttributes="false"
88488 colors="true"
88489 convertErrorsToExceptions="true"
88490 convertNoticesToExceptions="true"
88491 convertWarningsToExceptions="true"
88492 processIsolation="false"
88493 stopOnFailure="false"
88494 syntaxCheck="false"
88495 bootstrap="vendor/autoload.php"
88496 >
88497 <testsuites>
88498 <testsuite name="Symfony Process Component Test Suite">
88499 <directory>./Tests/</directory>
88500 </testsuite>
88501 </testsuites>
88502
88503 <filter>
88504 <whitelist>
88505 <directory>./</directory>
88506 <exclude>
88507 <directory>./Tests</directory>
88508 </exclude>
88509 </whitelist>
88510 </filter>
88511 </phpunit>
88512 <?php
88513
88514 /*
88515 * This file is part of the Symfony package.
88516 *
88517 * (c) Fabien Potencier <fabien@symfony.com>
88518 *
88519 * For the full copyright and license information, please view the LICENSE
88520 * file that was distributed with this source code.
88521 */
88522
88523 namespace Symfony\Component\Process;
88524
88525 use Symfony\Component\Process\Exception\InvalidArgumentException;
88526 use Symfony\Component\Process\Exception\LogicException;
88527 use Symfony\Component\Process\Exception\RuntimeException;
88528
88529 /**
88530 * Process is a thin wrapper around proc_* functions to ease
88531 * start independent PHP processes.
88532 *
88533 * @author Fabien Potencier <fabien@symfony.com>
88534 *
88535 * @api
88536 */
88537 class Process
88538 {
88539 const ERR = 'err';
88540 const OUT = 'out';
88541
88542 const STATUS_READY = 'ready';
88543 const STATUS_STARTED = 'started';
88544 const STATUS_TERMINATED = 'terminated';
88545
88546 const STDIN = 0;
88547 const STDOUT = 1;
88548 const STDERR = 2;
88549
88550 // Timeout Precision in seconds.
88551 const TIMEOUT_PRECISION = 0.2;
88552
88553 private $callback;
88554 private $commandline;
88555 private $cwd;
88556 private $env;
88557 private $stdin;
88558 private $starttime;
88559 private $timeout;
88560 private $options;
88561 private $exitcode;
88562 private $fallbackExitcode;
88563 private $processInformation;
88564 private $stdout;
88565 private $stderr;
88566 private $enhanceWindowsCompatibility;
88567 private $enhanceSigchildCompatibility;
88568 private $pipes;
88569 private $process;
88570 private $status = self::STATUS_READY;
88571 private $incrementalOutputOffset;
88572 private $incrementalErrorOutputOffset;
88573 private $tty;
88574
88575 private $fileHandles;
88576 private $readBytes;
88577
88578 private static $sigchild;
88579
88580 /**
88581 * Exit codes translation table.
88582 *
88583 * User-defined errors must use exit codes in the 64-113 range.
88584 *
88585 * @var array
88586 */
88587 public static $exitCodes = array(
88588 0 => 'OK',
88589 1 => 'General error',
88590 2 => 'Misuse of shell builtins',
88591
88592 126 => 'Invoked command cannot execute',
88593 127 => 'Command not found',
88594 128 => 'Invalid exit argument',
88595
88596 // signals
88597 129 => 'Hangup',
88598 130 => 'Interrupt',
88599 131 => 'Quit and dump core',
88600 132 => 'Illegal instruction',
88601 133 => 'Trace/breakpoint trap',
88602 134 => 'Process aborted',
88603 135 => 'Bus error: "access to undefined portion of memory object"',
88604 136 => 'Floating point exception: "erroneous arithmetic operation"',
88605 137 => 'Kill (terminate immediately)',
88606 138 => 'User-defined 1',
88607 139 => 'Segmentation violation',
88608 140 => 'User-defined 2',
88609 141 => 'Write to pipe with no one reading',
88610 142 => 'Signal raised by alarm',
88611 143 => 'Termination (request to terminate)',
88612 // 144 - not defined
88613 145 => 'Child process terminated, stopped (or continued*)',
88614 146 => 'Continue if stopped',
88615 147 => 'Stop executing temporarily',
88616 148 => 'Terminal stop signal',
88617 149 => 'Background process attempting to read from tty ("in")',
88618 150 => 'Background process attempting to write to tty ("out")',
88619 151 => 'Urgent data available on socket',
88620 152 => 'CPU time limit exceeded',
88621 153 => 'File size limit exceeded',
88622 154 => 'Signal raised by timer counting virtual time: "virtual timer expired"',
88623 155 => 'Profiling timer expired',
88624 // 156 - not defined
88625 157 => 'Pollable event',
88626 // 158 - not defined
88627 159 => 'Bad syscall',
88628 );
88629
88630 /**
88631 * Constructor.
88632 *
88633 * @param string $commandline The command line to run
88634 * @param string $cwd The working directory
88635 * @param array $env The environment variables or null to inherit
88636 * @param string $stdin The STDIN content
88637 * @param integer $timeout The timeout in seconds
88638 * @param array $options An array of options for proc_open
88639 *
88640 * @throws RuntimeException When proc_open is not installed
88641 *
88642 * @api
88643 */
88644 public function __construct($commandline, $cwd = null, array $env = null, $stdin = null, $timeout = 60, array $options = array())
88645 {
88646 if (!function_exists('proc_open')) {
88647 throw new RuntimeException('The Process class relies on proc_open, which is not available on your PHP installation.');
88648 }
88649
88650 $this->commandline = $commandline;
88651 $this->cwd = $cwd;
88652
88653 // on windows, if the cwd changed via chdir(), proc_open defaults to the dir where php was started
88654 // on gnu/linux, PHP builds with --enable-maintainer-zts are also affected
88655 // @see : https://bugs.php.net/bug.php?id=51800
88656 // @see : https://bugs.php.net/bug.php?id=50524
88657
88658 if (null === $this->cwd && (defined('ZEND_THREAD_SAFE') || defined('PHP_WINDOWS_VERSION_BUILD'))) {
88659 $this->cwd = getcwd();
88660 }
88661 if (null !== $env) {
88662 $this->setEnv($env);
88663 } else {
88664 $this->env = null;
88665 }
88666 $this->stdin = $stdin;
88667 $this->setTimeout($timeout);
88668 $this->enhanceWindowsCompatibility = true;
88669 $this->enhanceSigchildCompatibility = !defined('PHP_WINDOWS_VERSION_BUILD') && $this->isSigchildEnabled();
88670 $this->options = array_replace(array('suppress_errors' => true, 'binary_pipes' => true), $options);
88671 }
88672
88673 public function __destruct()
88674 {
88675 // stop() will check if we have a process running.
88676 $this->stop();
88677 }
88678
88679 public function __clone()
88680 {
88681 $this->resetProcessData();
88682 }
88683
88684 /**
88685 * Runs the process.
88686 *
88687 * The callback receives the type of output (out or err) and
88688 * some bytes from the output in real-time. It allows to have feedback
88689 * from the independent process during execution.
88690 *
88691 * The STDOUT and STDERR are also available after the process is finished
88692 * via the getOutput() and getErrorOutput() methods.
88693 *
88694 * @param callback|null $callback A PHP callback to run whenever there is some
88695 * output available on STDOUT or STDERR
88696 *
88697 * @return integer The exit status code
88698 *
88699 * @throws RuntimeException When process can't be launch or is stopped
88700 *
88701 * @api
88702 */
88703 public function run($callback = null)
88704 {
88705 $this->start($callback);
88706
88707 return $this->wait();
88708 }
88709
88710 /**
88711 * Starts the process and returns after sending the STDIN.
88712 *
88713 * This method blocks until all STDIN data is sent to the process then it
88714 * returns while the process runs in the background.
88715 *
88716 * The termination of the process can be awaited with wait().
88717 *
88718 * The callback receives the type of output (out or err) and some bytes from
88719 * the output in real-time while writing the standard input to the process.
88720 * It allows to have feedback from the independent process during execution.
88721 * If there is no callback passed, the wait() method can be called
88722 * with true as a second parameter then the callback will get all data occurred
88723 * in (and since) the start call.
88724 *
88725 * @param callback|null $callback A PHP callback to run whenever there is some
88726 * output available on STDOUT or STDERR
88727 *
88728 * @throws RuntimeException When process can't be launch or is stopped
88729 * @throws RuntimeException When process is already running
88730 */
88731 public function start($callback = null)
88732 {
88733 if ($this->isRunning()) {
88734 throw new RuntimeException('Process is already running');
88735 }
88736
88737 $this->resetProcessData();
88738 $this->starttime = microtime(true);
88739 $this->callback = $this->buildCallback($callback);
88740 $descriptors = $this->getDescriptors();
88741
88742 $commandline = $this->commandline;
88743
88744 if (defined('PHP_WINDOWS_VERSION_BUILD') && $this->enhanceWindowsCompatibility) {
88745 $commandline = 'cmd /V:ON /E:ON /C "'.$commandline.'"';
88746 if (!isset($this->options['bypass_shell'])) {
88747 $this->options['bypass_shell'] = true;
88748 }
88749 }
88750
88751 $this->process = proc_open($commandline, $descriptors, $this->pipes, $this->cwd, $this->env, $this->options);
88752
88753 if (!is_resource($this->process)) {
88754 throw new RuntimeException('Unable to launch a new process.');
88755 }
88756 $this->status = self::STATUS_STARTED;
88757
88758 foreach ($this->pipes as $pipe) {
88759 stream_set_blocking($pipe, false);
88760 }
88761
88762 $this->writePipes();
88763 $this->updateStatus(false);
88764 $this->checkTimeout();
88765 }
88766
88767 /**
88768 * Restarts the process.
88769 *
88770 * Be warned that the process is cloned before being started.
88771 *
88772 * @param callable $callback A PHP callback to run whenever there is some
88773 * output available on STDOUT or STDERR
88774 *
88775 * @return Process The new process
88776 *
88777 * @throws \RuntimeException When process can't be launch or is stopped
88778 * @throws \RuntimeException When process is already running
88779 *
88780 * @see start()
88781 */
88782 public function restart($callback = null)
88783 {
88784 if ($this->isRunning()) {
88785 throw new RuntimeException('Process is already running');
88786 }
88787
88788 $process = clone $this;
88789 $process->start($callback);
88790
88791 return $process;
88792 }
88793
88794 /**
88795 * Waits for the process to terminate.
88796 *
88797 * The callback receives the type of output (out or err) and some bytes
88798 * from the output in real-time while writing the standard input to the process.
88799 * It allows to have feedback from the independent process during execution.
88800 *
88801 * @param callback|null $callback A valid PHP callback
88802 *
88803 * @return integer The exitcode of the process
88804 *
88805 * @throws \RuntimeException When process timed out
88806 * @throws \RuntimeException When process stopped after receiving signal
88807 */
88808 public function wait($callback = null)
88809 {
88810 $this->updateStatus(false);
88811 if (null !== $callback) {
88812 $this->callback = $this->buildCallback($callback);
88813 }
88814 while ($this->pipes || (defined('PHP_WINDOWS_VERSION_BUILD') && $this->fileHandles)) {
88815 $this->checkTimeout();
88816 $this->readPipes(true);
88817 }
88818 $this->updateStatus(false);
88819 if ($this->processInformation['signaled']) {
88820 if ($this->isSigchildEnabled()) {
88821 throw new RuntimeException('The process has been signaled.');
88822 }
88823
88824 throw new RuntimeException(sprintf('The process has been signaled with signal "%s".', $this->processInformation['termsig']));
88825 }
88826
88827 $time = 0;
88828 while ($this->isRunning() && $time < 1000000) {
88829 $time += 1000;
88830 usleep(1000);
88831 }
88832
88833 if ($this->processInformation['signaled']) {
88834 if ($this->isSigchildEnabled()) {
88835 throw new RuntimeException('The process has been signaled.');
88836 }
88837
88838 throw new RuntimeException(sprintf('The process has been signaled with signal "%s".', $this->processInformation['termsig']));
88839 }
88840
88841 return $this->exitcode;
88842 }
88843
88844 /**
88845 * Returns the Pid (process identifier), if applicable.
88846 *
88847 * @return integer|null The process id if running, null otherwise
88848 *
88849 * @throws RuntimeException In case --enable-sigchild is activated
88850 */
88851 public function getPid()
88852 {
88853 if ($this->isSigchildEnabled()) {
88854 throw new RuntimeException('This PHP has been compiled with --enable-sigchild. The process identifier can not be retrieved.');
88855 }
88856
88857 $this->updateStatus(false);
88858
88859 return $this->isRunning() ? $this->processInformation['pid'] : null;
88860 }
88861
88862 /**
88863 * Sends a posix signal to the process.
88864 *
88865 * @param integer $signal A valid posix signal (see http://www.php.net/manual/en/pcntl.constants.php)
88866 * @return Process
88867 *
88868 * @throws LogicException In case the process is not running
88869 * @throws RuntimeException In case --enable-sigchild is activated
88870 * @throws RuntimeException In case of failure
88871 */
88872 public function signal($signal)
88873 {
88874 if (!$this->isRunning()) {
88875 throw new LogicException('Can not send signal on a non running process.');
88876 }
88877
88878 if ($this->isSigchildEnabled()) {
88879 throw new RuntimeException('This PHP has been compiled with --enable-sigchild. The process can not be signaled.');
88880 }
88881
88882 if (true !== @proc_terminate($this->process, $signal)) {
88883 throw new RuntimeException(sprintf('Error while sending signal `%d`.', $signal));
88884 }
88885
88886 return $this;
88887 }
88888
88889 /**
88890 * Returns the current output of the process (STDOUT).
88891 *
88892 * @return string The process output
88893 *
88894 * @api
88895 */
88896 public function getOutput()
88897 {
88898 $this->readPipes(false);
88899
88900 return $this->stdout;
88901 }
88902
88903 /**
88904 * Returns the output incrementally.
88905 *
88906 * In comparison with the getOutput method which always return the whole
88907 * output, this one returns the new output since the last call.
88908 *
88909 * @return string The process output since the last call
88910 */
88911 public function getIncrementalOutput()
88912 {
88913 $data = $this->getOutput();
88914
88915 $latest = substr($data, $this->incrementalOutputOffset);
88916 $this->incrementalOutputOffset = strlen($data);
88917
88918 return $latest;
88919 }
88920
88921 /**
88922 * Returns the current error output of the process (STDERR).
88923 *
88924 * @return string The process error output
88925 *
88926 * @api
88927 */
88928 public function getErrorOutput()
88929 {
88930 $this->readPipes(false);
88931
88932 return $this->stderr;
88933 }
88934
88935 /**
88936 * Returns the errorOutput incrementally.
88937 *
88938 * In comparison with the getErrorOutput method which always return the
88939 * whole error output, this one returns the new error output since the last
88940 * call.
88941 *
88942 * @return string The process error output since the last call
88943 */
88944 public function getIncrementalErrorOutput()
88945 {
88946 $data = $this->getErrorOutput();
88947
88948 $latest = substr($data, $this->incrementalErrorOutputOffset);
88949 $this->incrementalErrorOutputOffset = strlen($data);
88950
88951 return $latest;
88952 }
88953
88954 /**
88955 * Returns the exit code returned by the process.
88956 *
88957 * @return integer The exit status code
88958 *
88959 * @throws RuntimeException In case --enable-sigchild is activated and the sigchild compatibility mode is disabled
88960 *
88961 * @api
88962 */
88963 public function getExitCode()
88964 {
88965 if ($this->isSigchildEnabled() && !$this->enhanceSigchildCompatibility) {
88966 throw new RuntimeException('This PHP has been compiled with --enable-sigchild. You must use setEnhanceSigchildCompatibility() to use this method');
88967 }
88968
88969 $this->updateStatus(false);
88970
88971 return $this->exitcode;
88972 }
88973
88974 /**
88975 * Returns a string representation for the exit code returned by the process.
88976 *
88977 * This method relies on the Unix exit code status standardization
88978 * and might not be relevant for other operating systems.
88979 *
88980 * @return string A string representation for the exit status code
88981 *
88982 * @see http://tldp.org/LDP/abs/html/exitcodes.html
88983 * @see http://en.wikipedia.org/wiki/Unix_signal
88984 */
88985 public function getExitCodeText()
88986 {
88987 $exitcode = $this->getExitCode();
88988
88989 return isset(self::$exitCodes[$exitcode]) ? self::$exitCodes[$exitcode] : 'Unknown error';
88990 }
88991
88992 /**
88993 * Checks if the process ended successfully.
88994 *
88995 * @return Boolean true if the process ended successfully, false otherwise
88996 *
88997 * @api
88998 */
88999 public function isSuccessful()
89000 {
89001 return 0 === $this->getExitCode();
89002 }
89003
89004 /**
89005 * Returns true if the child process has been terminated by an uncaught signal.
89006 *
89007 * It always returns false on Windows.
89008 *
89009 * @return Boolean
89010 *
89011 * @throws RuntimeException In case --enable-sigchild is activated
89012 *
89013 * @api
89014 */
89015 public function hasBeenSignaled()
89016 {
89017 if ($this->isSigchildEnabled()) {
89018 throw new RuntimeException('This PHP has been compiled with --enable-sigchild. Term signal can not be retrieved');
89019 }
89020
89021 $this->updateStatus(false);
89022
89023 return $this->processInformation['signaled'];
89024 }
89025
89026 /**
89027 * Returns the number of the signal that caused the child process to terminate its execution.
89028 *
89029 * It is only meaningful if hasBeenSignaled() returns true.
89030 *
89031 * @return integer
89032 *
89033 * @throws RuntimeException In case --enable-sigchild is activated
89034 *
89035 * @api
89036 */
89037 public function getTermSignal()
89038 {
89039 if ($this->isSigchildEnabled()) {
89040 throw new RuntimeException('This PHP has been compiled with --enable-sigchild. Term signal can not be retrieved');
89041 }
89042
89043 $this->updateStatus(false);
89044
89045 return $this->processInformation['termsig'];
89046 }
89047
89048 /**
89049 * Returns true if the child process has been stopped by a signal.
89050 *
89051 * It always returns false on Windows.
89052 *
89053 * @return Boolean
89054 *
89055 * @api
89056 */
89057 public function hasBeenStopped()
89058 {
89059 $this->updateStatus(false);
89060
89061 return $this->processInformation['stopped'];
89062 }
89063
89064 /**
89065 * Returns the number of the signal that caused the child process to stop its execution.
89066 *
89067 * It is only meaningful if hasBeenStopped() returns true.
89068 *
89069 * @return integer
89070 *
89071 * @api
89072 */
89073 public function getStopSignal()
89074 {
89075 $this->updateStatus(false);
89076
89077 return $this->processInformation['stopsig'];
89078 }
89079
89080 /**
89081 * Checks if the process is currently running.
89082 *
89083 * @return Boolean true if the process is currently running, false otherwise
89084 */
89085 public function isRunning()
89086 {
89087 if (self::STATUS_STARTED !== $this->status) {
89088 return false;
89089 }
89090
89091 $this->updateStatus(false);
89092
89093 return $this->processInformation['running'];
89094 }
89095
89096 /**
89097 * Checks if the process has been started with no regard to the current state.
89098 *
89099 * @return Boolean true if status is ready, false otherwise
89100 */
89101 public function isStarted()
89102 {
89103 return $this->status != self::STATUS_READY;
89104 }
89105
89106 /**
89107 * Checks if the process is terminated.
89108 *
89109 * @return Boolean true if process is terminated, false otherwise
89110 */
89111 public function isTerminated()
89112 {
89113 $this->updateStatus(false);
89114
89115 return $this->status == self::STATUS_TERMINATED;
89116 }
89117
89118 /**
89119 * Gets the process status.
89120 *
89121 * The status is one of: ready, started, terminated.
89122 *
89123 * @return string The current process status
89124 */
89125 public function getStatus()
89126 {
89127 $this->updateStatus(false);
89128
89129 return $this->status;
89130 }
89131
89132 /**
89133 * Stops the process.
89134 *
89135 * @param integer|float $timeout The timeout in seconds
89136 * @param integer $signal A posix signal to send in case the process has not stop at timeout, default is SIGKILL
89137 *
89138 * @return integer The exit-code of the process
89139 *
89140 * @throws RuntimeException if the process got signaled
89141 */
89142 public function stop($timeout = 10, $signal = null)
89143 {
89144 $timeoutMicro = microtime(true) + $timeout;
89145 if ($this->isRunning()) {
89146 proc_terminate($this->process);
89147 do {
89148 usleep(1000);
89149 } while ($this->isRunning() && microtime(true) < $timeoutMicro);
89150
89151 if ($this->isRunning() && !$this->isSigchildEnabled()) {
89152 if (null !== $signal || defined('SIGKILL')) {
89153 $this->signal($signal ?: SIGKILL);
89154 }
89155 }
89156
89157 $this->updateStatus(false);
89158 }
89159 $this->status = self::STATUS_TERMINATED;
89160
89161 return $this->exitcode;
89162 }
89163
89164 /**
89165 * Adds a line to the STDOUT stream.
89166 *
89167 * @param string $line The line to append
89168 */
89169 public function addOutput($line)
89170 {
89171 $this->stdout .= $line;
89172 }
89173
89174 /**
89175 * Adds a line to the STDERR stream.
89176 *
89177 * @param string $line The line to append
89178 */
89179 public function addErrorOutput($line)
89180 {
89181 $this->stderr .= $line;
89182 }
89183
89184 /**
89185 * Gets the command line to be executed.
89186 *
89187 * @return string The command to execute
89188 */
89189 public function getCommandLine()
89190 {
89191 return $this->commandline;
89192 }
89193
89194 /**
89195 * Sets the command line to be executed.
89196 *
89197 * @param string $commandline The command to execute
89198 *
89199 * @return self The current Process instance
89200 */
89201 public function setCommandLine($commandline)
89202 {
89203 $this->commandline = $commandline;
89204
89205 return $this;
89206 }
89207
89208 /**
89209 * Gets the process timeout.
89210 *
89211 * @return integer|null The timeout in seconds or null if it's disabled
89212 */
89213 public function getTimeout()
89214 {
89215 return $this->timeout;
89216 }
89217
89218 /**
89219 * Sets the process timeout.
89220 *
89221 * To disable the timeout, set this value to null.
89222 *
89223 * @param float|null $timeout The timeout in seconds
89224 *
89225 * @return self The current Process instance
89226 *
89227 * @throws InvalidArgumentException if the timeout is negative
89228 */
89229 public function setTimeout($timeout)
89230 {
89231 if (null === $timeout) {
89232 $this->timeout = null;
89233
89234 return $this;
89235 }
89236
89237 $timeout = (float) $timeout;
89238
89239 if ($timeout < 0) {
89240 throw new InvalidArgumentException('The timeout value must be a valid positive integer or float number.');
89241 }
89242
89243 $this->timeout = $timeout;
89244
89245 return $this;
89246 }
89247
89248 /**
89249 * Enables or disables the TTY mode.
89250 *
89251 * @param boolean $tty True to enabled and false to disable
89252 *
89253 * @return self The current Process instance
89254 */
89255 public function setTty($tty)
89256 {
89257 $this->tty = (Boolean) $tty;
89258
89259 return $this;
89260 }
89261
89262 /**
89263 * Checks if the TTY mode is enabled.
89264 *
89265 * @return Boolean true if the TTY mode is enabled, false otherwise
89266 */
89267 public function isTty()
89268 {
89269 return $this->tty;
89270 }
89271
89272 /**
89273 * Gets the working directory.
89274 *
89275 * @return string The current working directory
89276 */
89277 public function getWorkingDirectory()
89278 {
89279 // This is for BC only
89280 if (null === $this->cwd) {
89281 // getcwd() will return false if any one of the parent directories does not have
89282 // the readable or search mode set, even if the current directory does
89283 return getcwd() ?: null;
89284 }
89285
89286 return $this->cwd;
89287 }
89288
89289 /**
89290 * Sets the current working directory.
89291 *
89292 * @param string $cwd The new working directory
89293 *
89294 * @return self The current Process instance
89295 */
89296 public function setWorkingDirectory($cwd)
89297 {
89298 $this->cwd = $cwd;
89299
89300 return $this;
89301 }
89302
89303 /**
89304 * Gets the environment variables.
89305 *
89306 * @return array The current environment variables
89307 */
89308 public function getEnv()
89309 {
89310 return $this->env;
89311 }
89312
89313 /**
89314 * Sets the environment variables.
89315 *
89316 * An environment variable value should be a string.
89317 * If it is an array, the variable is ignored.
89318 *
89319 * That happens in PHP when 'argv' is registered into
89320 * the $_ENV array for instance.
89321 *
89322 * @param array $env The new environment variables
89323 *
89324 * @return self The current Process instance
89325 */
89326 public function setEnv(array $env)
89327 {
89328 // Process can not handle env values that are arrays
89329 $env = array_filter($env, function ($value) { if (!is_array($value)) { return true; } });
89330
89331 $this->env = array();
89332 foreach ($env as $key => $value) {
89333 $this->env[(binary) $key] = (binary) $value;
89334 }
89335
89336 return $this;
89337 }
89338
89339 /**
89340 * Gets the contents of STDIN.
89341 *
89342 * @return string The current contents
89343 */
89344 public function getStdin()
89345 {
89346 return $this->stdin;
89347 }
89348
89349 /**
89350 * Sets the contents of STDIN.
89351 *
89352 * @param string $stdin The new contents
89353 *
89354 * @return self The current Process instance
89355 */
89356 public function setStdin($stdin)
89357 {
89358 $this->stdin = $stdin;
89359
89360 return $this;
89361 }
89362
89363 /**
89364 * Gets the options for proc_open.
89365 *
89366 * @return array The current options
89367 */
89368 public function getOptions()
89369 {
89370 return $this->options;
89371 }
89372
89373 /**
89374 * Sets the options for proc_open.
89375 *
89376 * @param array $options The new options
89377 *
89378 * @return self The current Process instance
89379 */
89380 public function setOptions(array $options)
89381 {
89382 $this->options = $options;
89383
89384 return $this;
89385 }
89386
89387 /**
89388 * Gets whether or not Windows compatibility is enabled.
89389 *
89390 * This is true by default.
89391 *
89392 * @return Boolean
89393 */
89394 public function getEnhanceWindowsCompatibility()
89395 {
89396 return $this->enhanceWindowsCompatibility;
89397 }
89398
89399 /**
89400 * Sets whether or not Windows compatibility is enabled.
89401 *
89402 * @param Boolean $enhance
89403 *
89404 * @return self The current Process instance
89405 */
89406 public function setEnhanceWindowsCompatibility($enhance)
89407 {
89408 $this->enhanceWindowsCompatibility = (Boolean) $enhance;
89409
89410 return $this;
89411 }
89412
89413 /**
89414 * Returns whether sigchild compatibility mode is activated or not.
89415 *
89416 * @return Boolean
89417 */
89418 public function getEnhanceSigchildCompatibility()
89419 {
89420 return $this->enhanceSigchildCompatibility;
89421 }
89422
89423 /**
89424 * Activates sigchild compatibility mode.
89425 *
89426 * Sigchild compatibility mode is required to get the exit code and
89427 * determine the success of a process when PHP has been compiled with
89428 * the --enable-sigchild option
89429 *
89430 * @param Boolean $enhance
89431 *
89432 * @return self The current Process instance
89433 */
89434 public function setEnhanceSigchildCompatibility($enhance)
89435 {
89436 $this->enhanceSigchildCompatibility = (Boolean) $enhance;
89437
89438 return $this;
89439 }
89440
89441 /**
89442 * Performs a check between the timeout definition and the time the process started.
89443 *
89444 * In case you run a background process (with the start method), you should
89445 * trigger this method regularly to ensure the process timeout
89446 *
89447 * @throws RuntimeException In case the timeout was reached
89448 */
89449 public function checkTimeout()
89450 {
89451 if (0 < $this->timeout && $this->timeout < microtime(true) - $this->starttime) {
89452 $this->stop(0);
89453
89454 throw new RuntimeException('The process timed-out.');
89455 }
89456 }
89457
89458 /**
89459 * Creates the descriptors needed by the proc_open.
89460 *
89461 * @return array
89462 */
89463 private function getDescriptors()
89464 {
89465 //Fix for PHP bug #51800: reading from STDOUT pipe hangs forever on Windows if the output is too big.
89466 //Workaround for this problem is to use temporary files instead of pipes on Windows platform.
89467 //@see https://bugs.php.net/bug.php?id=51800
89468 if (defined('PHP_WINDOWS_VERSION_BUILD')) {
89469 $this->fileHandles = array(
89470 self::STDOUT => tmpfile(),
89471 );
89472 if (false === $this->fileHandles[self::STDOUT]) {
89473 throw new RuntimeException('A temporary file could not be opened to write the process output to, verify that your TEMP environment variable is writable');
89474 }
89475 $this->readBytes = array(
89476 self::STDOUT => 0,
89477 );
89478
89479 return array(array('pipe', 'r'), $this->fileHandles[self::STDOUT], array('pipe', 'w'));
89480 }
89481
89482 if ($this->tty) {
89483 $descriptors = array(
89484 array('file', '/dev/tty', 'r'),
89485 array('file', '/dev/tty', 'w'),
89486 array('file', '/dev/tty', 'w'),
89487 );
89488 } else {
89489 $descriptors = array(
89490 array('pipe', 'r'), // stdin
89491 array('pipe', 'w'), // stdout
89492 array('pipe', 'w'), // stderr
89493 );
89494 }
89495
89496 if ($this->enhanceSigchildCompatibility && $this->isSigchildEnabled()) {
89497 // last exit code is output on the fourth pipe and caught to work around --enable-sigchild
89498 $descriptors = array_merge($descriptors, array(array('pipe', 'w')));
89499
89500 $this->commandline = '('.$this->commandline.') 3>/dev/null; code=$?; echo $code >&3; exit $code';
89501 }
89502
89503 return $descriptors;
89504 }
89505
89506 /**
89507 * Builds up the callback used by wait().
89508 *
89509 * The callbacks adds all occurred output to the specific buffer and calls
89510 * the user callback (if present) with the received output.
89511 *
89512 * @param callback|null $callback The user defined PHP callback
89513 *
89514 * @return callback A PHP callable
89515 */
89516 protected function buildCallback($callback)
89517 {
89518 $that = $this;
89519 $out = self::OUT;
89520 $err = self::ERR;
89521 $callback = function ($type, $data) use ($that, $callback, $out, $err) {
89522 if ($out == $type) {
89523 $that->addOutput($data);
89524 } else {
89525 $that->addErrorOutput($data);
89526 }
89527
89528 if (null !== $callback) {
89529 call_user_func($callback, $type, $data);
89530 }
89531 };
89532
89533 return $callback;
89534 }
89535
89536 /**
89537 * Updates the status of the process, reads pipes.
89538 *
89539 * @param Boolean $blocking Whether to use a clocking read call.
89540 */
89541 protected function updateStatus($blocking)
89542 {
89543 if (self::STATUS_STARTED !== $this->status) {
89544 return;
89545 }
89546
89547 $this->readPipes($blocking);
89548
89549 $this->processInformation = proc_get_status($this->process);
89550 $this->captureExitCode();
89551 if (!$this->processInformation['running']) {
89552 $this->close();
89553 $this->status = self::STATUS_TERMINATED;
89554 }
89555 }
89556
89557 /**
89558 * Returns whether PHP has been compiled with the '--enable-sigchild' option or not.
89559 *
89560 * @return Boolean
89561 */
89562 protected function isSigchildEnabled()
89563 {
89564 if (null !== self::$sigchild) {
89565 return self::$sigchild;
89566 }
89567
89568 ob_start();
89569 phpinfo(INFO_GENERAL);
89570
89571 return self::$sigchild = false !== strpos(ob_get_clean(), '--enable-sigchild');
89572 }
89573
89574 /**
89575 * Handles the windows file handles fallbacks.
89576 *
89577 * @param Boolean $closeEmptyHandles if true, handles that are empty will be assumed closed
89578 */
89579 private function processFileHandles($closeEmptyHandles = false)
89580 {
89581 $fh = $this->fileHandles;
89582 foreach ($fh as $type => $fileHandle) {
89583 fseek($fileHandle, $this->readBytes[$type]);
89584 $data = fread($fileHandle, 8192);
89585 if (strlen($data) > 0) {
89586 $this->readBytes[$type] += strlen($data);
89587 call_user_func($this->callback, $type == 1 ? self::OUT : self::ERR, $data);
89588 }
89589 if (false === $data || ($closeEmptyHandles && '' === $data && feof($fileHandle))) {
89590 fclose($fileHandle);
89591 unset($this->fileHandles[$type]);
89592 }
89593 }
89594 }
89595
89596 /**
89597 * Returns true if a system call has been interrupted.
89598 *
89599 * @return Boolean
89600 */
89601 private function hasSystemCallBeenInterrupted()
89602 {
89603 $lastError = error_get_last();
89604
89605 // stream_select returns false when the `select` system call is interrupted by an incoming signal
89606 return isset($lastError['message']) && false !== stripos($lastError['message'], 'interrupted system call');
89607 }
89608
89609 /**
89610 * Reads pipes, executes callback.
89611 *
89612 * @param Boolean $blocking Whether to use blocking calls or not.
89613 */
89614 private function readPipes($blocking)
89615 {
89616 if (defined('PHP_WINDOWS_VERSION_BUILD') && $this->fileHandles) {
89617 $this->processFileHandles(!$this->pipes);
89618 }
89619
89620 if ($this->pipes) {
89621 $r = $this->pipes;
89622 $w = null;
89623 $e = null;
89624
89625 // let's have a look if something changed in streams
89626 if (false === $n = @stream_select($r, $w, $e, 0, $blocking ? ceil(self::TIMEOUT_PRECISION * 1E6) : 0)) {
89627 // if a system call has been interrupted, forget about it, let's try again
89628 // otherwise, an error occured, let's reset pipes
89629 if (!$this->hasSystemCallBeenInterrupted()) {
89630 $this->pipes = array();
89631 }
89632
89633 return;
89634 }
89635
89636 // nothing has changed
89637 if (0 === $n) {
89638 return;
89639 }
89640
89641 $this->processReadPipes($r);
89642 }
89643 }
89644
89645 /**
89646 * Writes data to pipes.
89647 *
89648 * @param Boolean $blocking Whether to use blocking calls or not.
89649 */
89650 private function writePipes()
89651 {
89652 if ($this->tty) {
89653 $this->status = self::STATUS_TERMINATED;
89654
89655 return;
89656 }
89657
89658 if (null === $this->stdin) {
89659 fclose($this->pipes[0]);
89660 unset($this->pipes[0]);
89661
89662 return;
89663 }
89664
89665 $writePipes = array($this->pipes[0]);
89666 unset($this->pipes[0]);
89667 $stdinLen = strlen($this->stdin);
89668 $stdinOffset = 0;
89669
89670 while ($writePipes) {
89671 if (defined('PHP_WINDOWS_VERSION_BUILD')) {
89672 $this->processFileHandles();
89673 }
89674
89675 $r = $this->pipes;
89676 $w = $writePipes;
89677 $e = null;
89678
89679 if (false === $n = @stream_select($r, $w, $e, 0, $blocking ? ceil(static::TIMEOUT_PRECISION * 1E6) : 0)) {
89680 // if a system call has been interrupted, forget about it, let's try again
89681 if ($this->hasSystemCallBeenInterrupted()) {
89682 continue;
89683 }
89684 break;
89685 }
89686
89687 // nothing has changed, let's wait until the process is ready
89688 if (0 === $n) {
89689 continue;
89690 }
89691
89692 if ($w) {
89693 $written = fwrite($writePipes[0], (binary) substr($this->stdin, $stdinOffset), 8192);
89694 if (false !== $written) {
89695 $stdinOffset += $written;
89696 }
89697 if ($stdinOffset >= $stdinLen) {
89698 fclose($writePipes[0]);
89699 $writePipes = null;
89700 }
89701 }
89702
89703 $this->processReadPipes($r);
89704 }
89705 }
89706
89707 /**
89708 * Processes read pipes, executes callback on it.
89709 *
89710 * @param array $pipes
89711 */
89712 private function processReadPipes(array $pipes)
89713 {
89714 foreach ($pipes as $pipe) {
89715 $type = array_search($pipe, $this->pipes);
89716 $data = fread($pipe, 8192);
89717
89718 if (strlen($data) > 0) {
89719 // last exit code is output and caught to work around --enable-sigchild
89720 if (3 == $type) {
89721 $this->fallbackExitcode = (int) $data;
89722 } else {
89723 call_user_func($this->callback, $type == 1 ? self::OUT : self::ERR, $data);
89724 }
89725 }
89726 if (false === $data || feof($pipe)) {
89727 fclose($pipe);
89728 unset($this->pipes[$type]);
89729 }
89730 }
89731 }
89732
89733 /**
89734 * Captures the exitcode if mentioned in the process informations.
89735 */
89736 private function captureExitCode()
89737 {
89738 if (isset($this->processInformation['exitcode']) && -1 != $this->processInformation['exitcode']) {
89739 $this->exitcode = $this->processInformation['exitcode'];
89740 }
89741 }
89742
89743
89744 /**
89745 * Closes process resource, closes file handles, sets the exitcode.
89746 *
89747 * @return Integer The exitcode
89748 */
89749 private function close()
89750 {
89751 foreach ($this->pipes as $pipe) {
89752 fclose($pipe);
89753 }
89754
89755 $this->pipes = null;
89756 $exitcode = -1;
89757
89758 if (is_resource($this->process)) {
89759 $exitcode = proc_close($this->process);
89760 }
89761
89762 $this->exitcode = $this->exitcode !== null ? $this->exitcode : -1;
89763 $this->exitcode = -1 != $exitcode ? $exitcode : $this->exitcode;
89764
89765 if (-1 == $this->exitcode && null !== $this->fallbackExitcode) {
89766 $this->exitcode = $this->fallbackExitcode;
89767 } elseif (-1 === $this->exitcode && $this->processInformation['signaled'] && 0 < $this->processInformation['termsig']) {
89768 // if process has been signaled, no exitcode but a valid termsig, apply unix convention
89769 $this->exitcode = 128 + $this->processInformation['termsig'];
89770 }
89771
89772 if (defined('PHP_WINDOWS_VERSION_BUILD')) {
89773 foreach ($this->fileHandles as $fileHandle) {
89774 fclose($fileHandle);
89775 }
89776 $this->fileHandles = array();
89777 }
89778
89779 return $this->exitcode;
89780 }
89781
89782 /**
89783 * Resets data related to the latest run of the process.
89784 */
89785 private function resetProcessData()
89786 {
89787 $this->starttime = null;
89788 $this->callback = null;
89789 $this->exitcode = null;
89790 $this->fallbackExitcode = null;
89791 $this->processInformation = null;
89792 $this->stdout = null;
89793 $this->stderr = null;
89794 $this->pipes = null;
89795 $this->process = null;
89796 $this->status = self::STATUS_READY;
89797 $this->fileHandles = null;
89798 $this->readBytes = null;
89799 $this->incrementalOutputOffset = 0;
89800 $this->incrementalErrorOutputOffset = 0;
89801 }
89802 }
89803 PHP Option Type [![Build Status](https://secure.travis-ci.org/schmittjoh/php-option.png)](http://travis-ci.org/schmittjoh/php-option)
89804 ===============
89805 This adds an Option type for PHP.
89806
89807 The Option type is intended for cases where you sometimes might return a value
89808 (typically an object), and sometimes you might return no value (typically null)
89809 depending on arguments, or other runtime factors.
89810
89811 Often times, you forget to handle the case where no value is returned. Not intentionally
89812 of course, but maybe you did not account for all possible states of the system; or maybe you
89813 indeed covered all cases, then time goes on, code is refactored, some of these your checks
89814 might become invalid, or incomplete. Suddenly, without noticing, the no value case is not
89815 handled anymore. As a result, you might sometimes get fatal PHP errors telling you that
89816 you called a method on a non-object; users might see blank pages, or worse.
89817
89818 On one hand, the Option type forces a developer to consciously think about both cases
89819 (returning a value, or returning no value). That in itself will already make your code more
89820 robust. On the other hand, the Option type also allows the API developer to provide
89821 more concise API methods, and empowers the API user in how he consumes these methods.
89822
89823 Installation
89824 ============
89825 Installation is super-easy via composer
89826
89827 ```
89828 composer require phpoption/phpoption
89829 ```
89830
89831 or add it to your composer.json file.
89832
89833
89834 Usage
89835 =====
89836
89837 Using the Option Type in your API
89838 ---------------------------------
89839 ```php
89840 class MyRepository
89841 {
89842 public function findSomeEntity($criteria)
89843 {
89844 if (null !== $entity = $this->em->find(...)) {
89845 return new \PhpOption\Some($entity);
89846 }
89847
89848 // We use a singleton, for the None case.
89849 return \PhpOption\None::create();
89850 }
89851 }
89852 ```
89853
89854 If you are consuming an existing library, you can also use a shorter version
89855 which by default treats ``null`` as ``None``, and everything else as ``Some`` case:
89856
89857 ```php
89858 class MyRepository
89859 {
89860 public function findSomeEntity($criteria)
89861 {
89862 return \PhpOption\Option::fromValue($this->em->find(...));
89863
89864 // or, if you want to change the none value to false for example:
89865 return \PhpOption\Option::fromValue($this->em->find(...), false);
89866 }
89867 }
89868 ```
89869
89870 Case 1: You always Require an Entity in Calling Code
89871 ----------------------------------------------------
89872 ```php
89873 $entity = $repo->findSomeEntity(...)->get(); // returns entity, or throws exception
89874 ```
89875
89876 Case 2: Fallback to Default Value If Not Available
89877 --------------------------------------------------
89878 ```php
89879 $entity = $repo->findSomeEntity(...)->getOrElse(new Entity());
89880
89881 // Or, if you want to lazily create the entity.
89882 $entity = $repo->findSomeEntity(...)->getOrCall(function() {
89883 return new Entity();
89884 });
89885 ```
89886
89887 More Examples
89888 =============
89889
89890 No More Boiler Plate Code
89891 -------------------------
89892 ```php
89893 // Before
89894 if (null === $entity = $this->findSomeEntity()) {
89895 throw new NotFoundException();
89896 }
89897 echo $entity->name;
89898
89899 // After
89900 echo $this->findSomeEntity()->get()->name;
89901 ```
89902
89903 No More Control Flow Exceptions
89904 -------------------------------
89905 ```php
89906 // Before
89907 try {
89908 $entity = $this->findSomeEntity();
89909 } catch (NotFoundException $ex) {
89910 $entity = new Entity();
89911 }
89912
89913 // After
89914 $entity = $this->findSomeEntity()->getOrElse(new Entity());
89915 ```
89916
89917 More Concise Null Handling
89918 --------------------------
89919 ```php
89920 // Before
89921 $entity = $this->findSomeEntity();
89922 if (null === $entity) {
89923 return new Entity();
89924 }
89925
89926 return $entity;
89927
89928 // After
89929 return $this->findSomeEntity()->getOrElse(new Entity());
89930 ```
89931
89932 Trying Multiple Alternative Options
89933 -----------------------------------
89934 If you'd like to try multiple alternatives, the ``orElse`` method allows you to
89935 do this very elegantly:
89936
89937 ```php
89938 return $this->findSomeEntity()
89939 ->orElse($this->findSomeOtherEntity())
89940 ->orElse($this->createEntity());
89941 ```
89942 The first option which is non-empty will be returned. This is especially useful
89943 with lazy-evaluated options, see below.
89944
89945 Lazy-Evaluated Options
89946 ----------------------
89947 The above example has the flaw that we would need to evaluate all options when
89948 the method is called which creates unnecessary overhead if the first option is
89949 already non-empty.
89950
89951 Fortunately, we can easily solve this by using the ``LazyOption`` class:
89952
89953 ```php
89954 return $this->findSomeEntity()
89955 ->orElse(new LazyOption(array($this, 'findSomeOtherEntity')))
89956 ->orElse(new LazyOption(array($this, 'createEntity')));
89957 ```
89958
89959 This way, only the options that are necessary will actually be evaluated.
89960
89961
89962 Performance Considerations
89963 ==========================
89964 Of course, performance is important. Attached is a performance benchmark which
89965 you can run on a machine of your choosing.
89966
89967 The overhead incurred by the Option type comes down to the time that it takes to
89968 create one object, our wrapper. Also, we need to perform one additional method call
89969 to retrieve the value from the wrapper.
89970
89971 * Overhead: Creation of 1 Object, and 1 Method Call
89972 * Average Overhead per Invocation (some case/value returned): 0.000000761s (that is 761 nano seconds)
89973 * Average Overhead per Invocation (none case/null returned): 0.000000368s (that is 368 nano seconds)
89974
89975 The benchmark was run under Ubuntu precise with PHP 5.4.6. As you can see the
89976 overhead is surprisingly low, almost negligible.
89977
89978 So in conclusion, unless you plan to call a method thousands of times during a
89979 request, there is no reason to stick to the ``object|null`` return value; better give
89980 your code some options!
89981 Apache License
89982 Version 2.0, January 2004
89983 http://www.apache.org/licenses/
89984
89985 TERMS AND CONDITIONS FOR USE, REPRODUCTION, AND DISTRIBUTION
89986
89987 1. Definitions.
89988
89989 "License" shall mean the terms and conditions for use, reproduction,
89990 and distribution as defined by Sections 1 through 9 of this document.
89991
89992 "Licensor" shall mean the copyright owner or entity authorized by
89993 the copyright owner that is granting the License.
89994
89995 "Legal Entity" shall mean the union of the acting entity and all
89996 other entities that control, are controlled by, or are under common
89997 control with that entity. For the purposes of this definition,
89998 "control" means (i) the power, direct or indirect, to cause the
89999 direction or management of such entity, whether by contract or
90000 otherwise, or (ii) ownership of fifty percent (50%) or more of the
90001 outstanding shares, or (iii) beneficial ownership of such entity.
90002
90003 "You" (or "Your") shall mean an individual or Legal Entity
90004 exercising permissions granted by this License.
90005
90006 "Source" form shall mean the preferred form for making modifications,
90007 including but not limited to software source code, documentation
90008 source, and configuration files.
90009
90010 "Object" form shall mean any form resulting from mechanical
90011 transformation or translation of a Source form, including but
90012 not limited to compiled object code, generated documentation,
90013 and conversions to other media types.
90014
90015 "Work" shall mean the work of authorship, whether in Source or
90016 Object form, made available under the License, as indicated by a
90017 copyright notice that is included in or attached to the work
90018 (an example is provided in the Appendix below).
90019
90020 "Derivative Works" shall mean any work, whether in Source or Object
90021 form, that is based on (or derived from) the Work and for which the
90022 editorial revisions, annotations, elaborations, or other modifications
90023 represent, as a whole, an original work of authorship. For the purposes
90024 of this License, Derivative Works shall not include works that remain
90025 separable from, or merely link (or bind by name) to the interfaces of,
90026 the Work and Derivative Works thereof.
90027
90028 "Contribution" shall mean any work of authorship, including
90029 the original version of the Work and any modifications or additions
90030 to that Work or Derivative Works thereof, that is intentionally
90031 submitted to Licensor for inclusion in the Work by the copyright owner
90032 or by an individual or Legal Entity authorized to submit on behalf of
90033 the copyright owner. For the purposes of this definition, "submitted"
90034 means any form of electronic, verbal, or written communication sent
90035 to the Licensor or its representatives, including but not limited to
90036 communication on electronic mailing lists, source code control systems,
90037 and issue tracking systems that are managed by, or on behalf of, the
90038 Licensor for the purpose of discussing and improving the Work, but
90039 excluding communication that is conspicuously marked or otherwise
90040 designated in writing by the copyright owner as "Not a Contribution."
90041
90042 "Contributor" shall mean Licensor and any individual or Legal Entity
90043 on behalf of whom a Contribution has been received by Licensor and
90044 subsequently incorporated within the Work.
90045
90046 2. Grant of Copyright License. Subject to the terms and conditions of
90047 this License, each Contributor hereby grants to You a perpetual,
90048 worldwide, non-exclusive, no-charge, royalty-free, irrevocable
90049 copyright license to reproduce, prepare Derivative Works of,
90050 publicly display, publicly perform, sublicense, and distribute the
90051 Work and such Derivative Works in Source or Object form.
90052
90053 3. Grant of Patent License. Subject to the terms and conditions of
90054 this License, each Contributor hereby grants to You a perpetual,
90055 worldwide, non-exclusive, no-charge, royalty-free, irrevocable
90056 (except as stated in this section) patent license to make, have made,
90057 use, offer to sell, sell, import, and otherwise transfer the Work,
90058 where such license applies only to those patent claims licensable
90059 by such Contributor that are necessarily infringed by their
90060 Contribution(s) alone or by combination of their Contribution(s)
90061 with the Work to which such Contribution(s) was submitted. If You
90062 institute patent litigation against any entity (including a
90063 cross-claim or counterclaim in a lawsuit) alleging that the Work
90064 or a Contribution incorporated within the Work constitutes direct
90065 or contributory patent infringement, then any patent licenses
90066 granted to You under this License for that Work shall terminate
90067 as of the date such litigation is filed.
90068
90069 4. Redistribution. You may reproduce and distribute copies of the
90070 Work or Derivative Works thereof in any medium, with or without
90071 modifications, and in Source or Object form, provided that You
90072 meet the following conditions:
90073
90074 (a) You must give any other recipients of the Work or
90075 Derivative Works a copy of this License; and
90076
90077 (b) You must cause any modified files to carry prominent notices
90078 stating that You changed the files; and
90079
90080 (c) You must retain, in the Source form of any Derivative Works
90081 that You distribute, all copyright, patent, trademark, and
90082 attribution notices from the Source form of the Work,
90083 excluding those notices that do not pertain to any part of
90084 the Derivative Works; and
90085
90086 (d) If the Work includes a "NOTICE" text file as part of its
90087 distribution, then any Derivative Works that You distribute must
90088 include a readable copy of the attribution notices contained
90089 within such NOTICE file, excluding those notices that do not
90090 pertain to any part of the Derivative Works, in at least one
90091 of the following places: within a NOTICE text file distributed
90092 as part of the Derivative Works; within the Source form or
90093 documentation, if provided along with the Derivative Works; or,
90094 within a display generated by the Derivative Works, if and
90095 wherever such third-party notices normally appear. The contents
90096 of the NOTICE file are for informational purposes only and
90097 do not modify the License. You may add Your own attribution
90098 notices within Derivative Works that You distribute, alongside
90099 or as an addendum to the NOTICE text from the Work, provided
90100 that such additional attribution notices cannot be construed
90101 as modifying the License.
90102
90103 You may add Your own copyright statement to Your modifications and
90104 may provide additional or different license terms and conditions
90105 for use, reproduction, or distribution of Your modifications, or
90106 for any such Derivative Works as a whole, provided Your use,
90107 reproduction, and distribution of the Work otherwise complies with
90108 the conditions stated in this License.
90109
90110 5. Submission of Contributions. Unless You explicitly state otherwise,
90111 any Contribution intentionally submitted for inclusion in the Work
90112 by You to the Licensor shall be under the terms and conditions of
90113 this License, without any additional terms or conditions.
90114 Notwithstanding the above, nothing herein shall supersede or modify
90115 the terms of any separate license agreement you may have executed
90116 with Licensor regarding such Contributions.
90117
90118 6. Trademarks. This License does not grant permission to use the trade
90119 names, trademarks, service marks, or product names of the Licensor,
90120 except as required for reasonable and customary use in describing the
90121 origin of the Work and reproducing the content of the NOTICE file.
90122
90123 7. Disclaimer of Warranty. Unless required by applicable law or
90124 agreed to in writing, Licensor provides the Work (and each
90125 Contributor provides its Contributions) on an "AS IS" BASIS,
90126 WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or
90127 implied, including, without limitation, any warranties or conditions
90128 of TITLE, NON-INFRINGEMENT, MERCHANTABILITY, or FITNESS FOR A
90129 PARTICULAR PURPOSE. You are solely responsible for determining the
90130 appropriateness of using or redistributing the Work and assume any
90131 risks associated with Your exercise of permissions under this License.
90132
90133 8. Limitation of Liability. In no event and under no legal theory,
90134 whether in tort (including negligence), contract, or otherwise,
90135 unless required by applicable law (such as deliberate and grossly
90136 negligent acts) or agreed to in writing, shall any Contributor be
90137 liable to You for damages, including any direct, indirect, special,
90138 incidental, or consequential damages of any character arising as a
90139 result of this License or out of the use or inability to use the
90140 Work (including but not limited to damages for loss of goodwill,
90141 work stoppage, computer failure or malfunction, or any and all
90142 other commercial damages or losses), even if such Contributor
90143 has been advised of the possibility of such damages.
90144
90145 9. Accepting Warranty or Additional Liability. While redistributing
90146 the Work or Derivative Works thereof, You may choose to offer,
90147 and charge a fee for, acceptance of support, warranty, indemnity,
90148 or other liability obligations and/or rights consistent with this
90149 License. However, in accepting such obligations, You may act only
90150 on Your own behalf and on Your sole responsibility, not on behalf
90151 of any other Contributor, and only if You agree to indemnify,
90152 defend, and hold each Contributor harmless for any liability
90153 incurred by, or claims asserted against, such Contributor by reason
90154 of your accepting any such warranty or additional liability.
90155
90156 END OF TERMS AND CONDITIONS
90157
90158 APPENDIX: How to apply the Apache License to your work.
90159
90160 To apply the Apache License to your work, attach the following
90161 boilerplate notice, with the fields enclosed by brackets "[]"
90162 replaced with your own identifying information. (Don't include
90163 the brackets!) The text should be enclosed in the appropriate
90164 comment syntax for the file format. We also recommend that a
90165 file or class name and description of purpose be included on the
90166 same "printed page" as the copyright notice for easier
90167 identification within third-party archives.
90168
90169 Copyright [yyyy] [name of copyright owner]
90170
90171 Licensed under the Apache License, Version 2.0 (the "License");
90172 you may not use this file except in compliance with the License.
90173 You may obtain a copy of the License at
90174
90175 http://www.apache.org/licenses/LICENSE-2.0
90176
90177 Unless required by applicable law or agreed to in writing, software
90178 distributed under the License is distributed on an "AS IS" BASIS,
90179 WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
90180 See the License for the specific language governing permissions and
90181 limitations under the License.<?php
90182
90183 /*
90184 * Copyright 2012 Johannes M. Schmitt <schmittjoh@gmail.com>
90185 *
90186 * Licensed under the Apache License, Version 2.0 (the "License");
90187 * you may not use this file except in compliance with the License.
90188 * You may obtain a copy of the License at
90189 *
90190 * http://www.apache.org/licenses/LICENSE-2.0
90191 *
90192 * Unless required by applicable law or agreed to in writing, software
90193 * distributed under the License is distributed on an "AS IS" BASIS,
90194 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
90195 * See the License for the specific language governing permissions and
90196 * limitations under the License.
90197 */
90198
90199 namespace PhpOption;
90200
90201 /**
90202 * Base Option Class.
90203 *
90204 * @author Johannes M. Schmitt <schmittjoh@gmail.com>
90205 */
90206 abstract class Option
90207 {
90208 /**
90209 * Creates an option given a return value.
90210 *
90211 * This is intended for consuming existing APIs and allows you to easily
90212 * convert them to an option. By default, we treat ``null`` as the None case,
90213 * and everything else as Some.
90214 *
90215 * @param mixed $value The actual return value.
90216 * @param mixed $noneValue The value which should be considered "None"; null
90217 * by default.
90218 *
90219 * @return Option
90220 */
90221 public static function fromValue($value, $noneValue = null)
90222 {
90223 if ($value === $noneValue) {
90224 return None::create();
90225 }
90226
90227 return new Some($value);
90228 }
90229
90230 /**
90231 * Creates a lazy-option with the given callback.
90232 *
90233 * This is also a helper constructor for lazy-consuming existing APIs where
90234 * the return value is not yet an option. By default, we treat ``null`` as
90235 * None case, and everything else as Some.
90236 *
90237 * @param callable $callback The callback to evaluate.
90238 * @param array $arguments
90239 * @param mixed $noneValue The value which should be considered "None"; null
90240 * by default.
90241 *
90242 * @return Option
90243 */
90244 public static function fromReturn($callback, array $arguments = array(), $noneValue = null)
90245 {
90246 return new LazyOption(function() use ($callback, $arguments, $noneValue) {
90247 $return = call_user_func_array($callback, $arguments);
90248
90249 if ($return === $noneValue) {
90250 return None::create();
90251 }
90252
90253 return new Some($return);
90254 });
90255 }
90256
90257 /**
90258 * Returns the value if available, or throws an exception otherwise.
90259 *
90260 * @throws \RuntimeException if value is not available
90261 *
90262 * @return mixed
90263 */
90264 abstract public function get();
90265
90266 /**
90267 * Returns the value if available, or the default value if not.
90268 *
90269 * @param mixed $default
90270 *
90271 * @return mixed
90272 */
90273 abstract public function getOrElse($default);
90274
90275 /**
90276 * Returns the value if available, or the results of the callable.
90277 *
90278 * This is preferable over ``getOrElse`` if the computation of the default
90279 * value is expensive.
90280 *
90281 * @param callable $callable
90282 *
90283 * @return mixed
90284 */
90285 abstract public function getOrCall($callable);
90286
90287 /**
90288 * Returns the value if available, or throws the passed exception.
90289 *
90290 * @param \Exception $ex
90291 *
90292 * @return mixed
90293 */
90294 abstract public function getOrThrow(\Exception $ex);
90295
90296 /**
90297 * Returns true if no value is available, false otherwise.
90298 *
90299 * @return boolean
90300 */
90301 abstract public function isEmpty();
90302
90303 /**
90304 * Returns true if a value is available, false otherwise.
90305 *
90306 * @return boolean
90307 */
90308 abstract public function isDefined();
90309
90310 /**
90311 * Returns this option if non-empty, or the passed option otherwise.
90312 *
90313 * This can be used to try multiple alternatives, and is especially useful
90314 * with lazy evaluating options:
90315 *
90316 * ```php
90317 * $repo->findSomething()
90318 * ->orElse(new LazyOption(array($repo, 'findSomethingElse')))
90319 * ->orElse(new LazyOption(array($repo, 'createSomething')));
90320 * ```
90321 *
90322 * @param Option $else
90323 *
90324 * @return Option
90325 */
90326 abstract public function orElse(Option $else);
90327
90328 /**
90329 * This is similar to map() below except that the return value has no meaning;
90330 * the passed callable is simply executed if the option is non-empty, and
90331 * ignored if the option is empty.
90332 *
90333 * In all cases, the return value of the callable is discarded.
90334 *
90335 * ```php
90336 * $comment->getMaybeFile()->ifDefined(function($file) {
90337 * // Do something with $file here.
90338 * });
90339 * ```
90340 *
90341 * If you're looking for something like ``ifEmpty``, you can use ``getOrCall``
90342 * and ``getOrElse`` in these cases.
90343 *
90344 * @deprecated Use forAll() instead.
90345 *
90346 * @param callable $callable
90347 *
90348 * @return void
90349 */
90350 abstract public function ifDefined($callable);
90351
90352 /**
90353 * This is similar to map() except that the return value of the callable has no meaning.
90354 *
90355 * The passed callable is simply executed if the option is non-empty, and ignored if the
90356 * option is empty. This method is preferred for callables with side-effects, while map()
90357 * is intended for callables without side-effects.
90358 *
90359 * @param callable $callable
90360 *
90361 * @return Option
90362 */
90363 abstract public function forAll($callable);
90364
90365 /**
90366 * Applies the callable to the value of the option if it is non-empty,
90367 * and returns the return value of the callable wrapped in Some().
90368 *
90369 * If the option is empty, then the callable is not applied.
90370 *
90371 * ```php
90372 * (new Some("foo"))->map('strtoupper')->get(); // "FOO"
90373 * ```
90374 *
90375 * @param callable $callable
90376 *
90377 * @return Option
90378 */
90379 abstract public function map($callable);
90380
90381 /**
90382 * Applies the callable to the value of the option if it is non-empty, and
90383 * returns the return value of the callable directly.
90384 *
90385 * In contrast to ``map``, the return value of the callable is expected to
90386 * be an Option itself; it is not automatically wrapped in Some().
90387 *
90388 * @param callable $callable must return an Option
90389 *
90390 * @return Option
90391 */
90392 abstract public function flatMap($callable);
90393
90394 /**
90395 * If the option is empty, it is returned immediately without applying the callable.
90396 *
90397 * If the option is non-empty, the callable is applied, and if it returns true,
90398 * the option itself is returned; otherwise, None is returned.
90399 *
90400 * @param callable $callable
90401 *
90402 * @return Option
90403 */
90404 abstract public function filter($callable);
90405
90406 /**
90407 * If the option is empty, it is returned immediately without applying the callable.
90408 *
90409 * If the option is non-empty, the callable is applied, and if it returns false,
90410 * the option itself is returned; otherwise, None is returned.
90411 *
90412 * @param callable $callable
90413 *
90414 * @return Option
90415 */
90416 abstract public function filterNot($callable);
90417
90418 /**
90419 * If the option is empty, it is returned immediately.
90420 *
90421 * If the option is non-empty, and its value does not equal the passed value
90422 * (via a shallow comparison ===), then None is returned. Otherwise, the
90423 * Option is returned.
90424 *
90425 * In other words, this will filter all but the passed value.
90426 *
90427 * @param mixed $value
90428 *
90429 * @return Option
90430 */
90431 abstract public function select($value);
90432
90433 /**
90434 * If the option is empty, it is returned immediately.
90435 *
90436 * If the option is non-empty, and its value does equal the passed value (via
90437 * a shallow comparison ===), then None is returned; otherwise, the Option is
90438 * returned.
90439 *
90440 * In other words, this will let all values through expect the passed value.
90441 *
90442 * @param mixed $value
90443 *
90444 * @return Option
90445 */
90446 abstract public function reject($value);
90447 }
90448 <?php
90449
90450 /*
90451 * Copyright 2012 Johannes M. Schmitt <schmittjoh@gmail.com>
90452 *
90453 * Licensed under the Apache License, Version 2.0 (the "License");
90454 * you may not use this file except in compliance with the License.
90455 * You may obtain a copy of the License at
90456 *
90457 * http://www.apache.org/licenses/LICENSE-2.0
90458 *
90459 * Unless required by applicable law or agreed to in writing, software
90460 * distributed under the License is distributed on an "AS IS" BASIS,
90461 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
90462 * See the License for the specific language governing permissions and
90463 * limitations under the License.
90464 */
90465
90466 namespace PhpOption;
90467
90468 final class LazyOption extends Option
90469 {
90470 /** @var callable */
90471 private $callback;
90472
90473 /** @var array */
90474 private $arguments;
90475
90476 /** @var Option|null */
90477 private $option;
90478
90479 /**
90480 * Helper Constructor.
90481 *
90482 * @param callable $callback
90483 * @param array $arguments
90484 *
90485 * @return LazyOption
90486 */
90487 public static function create($callback, array $arguments = array())
90488 {
90489 return new self($callback, $arguments);
90490 }
90491
90492 /**
90493 * Constructor.
90494 *
90495 * @param callable $callback
90496 * @param array $arguments
90497 */
90498 public function __construct($callback, array $arguments = array())
90499 {
90500 if (!is_callable($callback)) {
90501 throw new \InvalidArgumentException('Invalid callback given');
90502 }
90503
90504 $this->callback = $callback;
90505 $this->arguments = $arguments;
90506 }
90507
90508 public function isDefined()
90509 {
90510 return $this->option()->isDefined();
90511 }
90512
90513 public function isEmpty()
90514 {
90515 return $this->option()->isEmpty();
90516 }
90517
90518 public function get()
90519 {
90520 return $this->option()->get();
90521 }
90522
90523 public function getOrElse($default)
90524 {
90525 return $this->option()->getOrElse($default);
90526 }
90527
90528 public function getOrCall($callable)
90529 {
90530 return $this->option()->getOrCall($callable);
90531 }
90532
90533 public function getOrThrow(\Exception $ex)
90534 {
90535 return $this->option()->getOrThrow($ex);
90536 }
90537
90538 public function orElse(Option $else)
90539 {
90540 return $this->option()->orElse($else);
90541 }
90542
90543 /**
90544 * @deprecated Use forAll() instead.
90545 */
90546 public function ifDefined($callable)
90547 {
90548 $this->option()->ifDefined($callable);
90549 }
90550
90551 public function forAll($callable)
90552 {
90553 return $this->option()->forAll($callable);
90554 }
90555
90556 public function map($callable)
90557 {
90558 return $this->option()->map($callable);
90559 }
90560
90561 public function flatMap($callable)
90562 {
90563 return $this->option()->flatMap($callable);
90564 }
90565
90566 public function filter($callable)
90567 {
90568 return $this->option()->filter($callable);
90569 }
90570
90571 public function filterNot($callable)
90572 {
90573 return $this->option()->filterNot($callable);
90574 }
90575
90576 public function select($value)
90577 {
90578 return $this->option()->select($value);
90579 }
90580
90581 public function reject($value)
90582 {
90583 return $this->option()->reject($value);
90584 }
90585
90586 /**
90587 * @return Option
90588 */
90589 private function option()
90590 {
90591 if ($this->option === null) {
90592 $this->option = call_user_func_array($this->callback, $this->arguments);
90593 if (!$this->option instanceof Option) {
90594 $this->option = null;
90595 throw new \RuntimeException('Expected instance of \PhpOption\Option');
90596 }
90597 }
90598
90599 return $this->option;
90600 }
90601 }
90602 <?php
90603
90604 /*
90605 * Copyright 2012 Johannes M. Schmitt <schmittjoh@gmail.com>
90606 *
90607 * Licensed under the Apache License, Version 2.0 (the "License");
90608 * you may not use this file except in compliance with the License.
90609 * You may obtain a copy of the License at
90610 *
90611 * http://www.apache.org/licenses/LICENSE-2.0
90612 *
90613 * Unless required by applicable law or agreed to in writing, software
90614 * distributed under the License is distributed on an "AS IS" BASIS,
90615 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
90616 * See the License for the specific language governing permissions and
90617 * limitations under the License.
90618 */
90619
90620 namespace PhpOption;
90621
90622 final class Some extends Option
90623 {
90624 private $value;
90625
90626 public function __construct($value)
90627 {
90628 $this->value = $value;
90629 }
90630
90631 public static function create($value)
90632 {
90633 return new self($value);
90634 }
90635
90636 public function isDefined()
90637 {
90638 return true;
90639 }
90640
90641 public function isEmpty()
90642 {
90643 return false;
90644 }
90645
90646 public function get()
90647 {
90648 return $this->value;
90649 }
90650
90651 public function getOrElse($default)
90652 {
90653 return $this->value;
90654 }
90655
90656 public function getOrCall($callable)
90657 {
90658 return $this->value;
90659 }
90660
90661 public function getOrThrow(\Exception $ex)
90662 {
90663 return $this->value;
90664 }
90665
90666 public function orElse(Option $else)
90667 {
90668 return $this;
90669 }
90670
90671 /**
90672 * @deprecated Use forAll() instead.
90673 */
90674 public function ifDefined($callable)
90675 {
90676 call_user_func($callable, $this->value);
90677 }
90678
90679 public function forAll($callable)
90680 {
90681 call_user_func($callable, $this->value);
90682
90683 return $this;
90684 }
90685
90686 public function map($callable)
90687 {
90688 return new self(call_user_func($callable, $this->value));
90689 }
90690
90691 public function flatMap($callable)
90692 {
90693 $rs = call_user_func($callable, $this->value);
90694 if ( ! $rs instanceof Option) {
90695 throw new \RuntimeException('Callables passed to flatMap() must return an Option. Maybe you should use map() instead?');
90696 }
90697
90698 return $rs;
90699 }
90700
90701 public function filter($callable)
90702 {
90703 if (true === call_user_func($callable, $this->value)) {
90704 return $this;
90705 }
90706
90707 return None::create();
90708 }
90709
90710 public function filterNot($callable)
90711 {
90712 if (false === call_user_func($callable, $this->value)) {
90713 return $this;
90714 }
90715
90716 return None::create();
90717 }
90718
90719 public function select($value)
90720 {
90721 if ($this->value === $value) {
90722 return $this;
90723 }
90724
90725 return None::create();
90726 }
90727
90728 public function reject($value)
90729 {
90730 if ($this->value === $value) {
90731 return None::create();
90732 }
90733
90734 return $this;
90735 }
90736 }
90737 <?php
90738
90739 /*
90740 * Copyright 2012 Johannes M. Schmitt <schmittjoh@gmail.com>
90741 *
90742 * Licensed under the Apache License, Version 2.0 (the "License");
90743 * you may not use this file except in compliance with the License.
90744 * You may obtain a copy of the License at
90745 *
90746 * http://www.apache.org/licenses/LICENSE-2.0
90747 *
90748 * Unless required by applicable law or agreed to in writing, software
90749 * distributed under the License is distributed on an "AS IS" BASIS,
90750 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
90751 * See the License for the specific language governing permissions and
90752 * limitations under the License.
90753 */
90754
90755 namespace PhpOption;
90756
90757 final class None extends Option
90758 {
90759 private static $instance;
90760
90761 public static function create()
90762 {
90763 if (null === self::$instance) {
90764 self::$instance = new self();
90765 }
90766
90767 return self::$instance;
90768 }
90769
90770 public function get()
90771 {
90772 throw new \RuntimeException('None has no value.');
90773 }
90774
90775 public function getOrCall($callable)
90776 {
90777 return call_user_func($callable);
90778 }
90779
90780 public function getOrElse($default)
90781 {
90782 return $default;
90783 }
90784
90785 public function getOrThrow(\Exception $ex)
90786 {
90787 throw $ex;
90788 }
90789
90790 public function isEmpty()
90791 {
90792 return true;
90793 }
90794
90795 public function isDefined()
90796 {
90797 return false;
90798 }
90799
90800 public function orElse(Option $else)
90801 {
90802 return $else;
90803 }
90804
90805 /**
90806 * @deprecated Use forAll() instead.
90807 */
90808 public function ifDefined($callable)
90809 {
90810 // Just do nothing in that case.
90811 }
90812
90813 public function forAll($callable)
90814 {
90815 return $this;
90816 }
90817
90818 public function map($callable)
90819 {
90820 return $this;
90821 }
90822
90823 public function flatMap($callable)
90824 {
90825 return $this;
90826 }
90827
90828 public function filter($callable)
90829 {
90830 return $this;
90831 }
90832
90833 public function filterNot($callable)
90834 {
90835 return $this;
90836 }
90837
90838 public function select($value)
90839 {
90840 return $this;
90841 }
90842
90843 public function reject($value)
90844 {
90845 return $this;
90846 }
90847
90848 private function __construct() { }
90849 }<?php
90850
90851 if ( ! is_file($autoloadFile = __DIR__.'/../vendor/autoload.php')) {
90852 echo 'Could not find "vendor/autoload.php". Did you forget to run "composer install --dev"?'.PHP_EOL;
90853 exit(1);
90854 }
90855
90856 require_once $autoloadFile;<?php
90857
90858 namespace PhpOption\Tests;
90859
90860 class OptionTest extends \PHPUnit_Framework_TestCase
90861 {
90862 public function testfromValueWithDefaultNoneValue()
90863 {
90864 $this->assertInstanceOf('PhpOption\None', \PhpOption\Option::fromValue(null));
90865 $this->assertInstanceOf('PhpOption\Some', \PhpOption\Option::fromValue('value'));
90866 }
90867
90868 public function testFromValueWithFalseNoneValue()
90869 {
90870 $this->assertInstanceOf('PhpOption\None', \PhpOption\Option::fromValue(false, false));
90871 $this->assertInstanceOf('PhpOption\Some', \PhpOption\Option::fromValue('value', false));
90872 $this->assertInstanceOf('PhpOption\Some', \PhpOption\Option::fromValue(null, false));
90873 }
90874
90875 public function testFromReturn()
90876 {
90877 $null = function() { return null; };
90878 $false = function() { return false; };
90879 $some = function() { return 'foo'; };
90880
90881 $this->assertTrue(\PhpOption\Option::fromReturn($null)->isEmpty());
90882 $this->assertFalse(\PhpOption\Option::fromReturn($false)->isEmpty());
90883 $this->assertTrue(\PhpOption\Option::fromReturn($false, array(), false)->isEmpty());
90884 $this->assertTrue(\PhpOption\Option::fromReturn($some)->isDefined());
90885 $this->assertFalse(\PhpOption\Option::fromReturn($some, array(), 'foo')->isDefined());
90886 }
90887
90888 public function testOrElse()
90889 {
90890 $a = new \PhpOption\Some('a');
90891 $b = new \PhpOption\Some('b');
90892
90893 $this->assertEquals('a', $a->orElse($b)->get());
90894 }
90895
90896 public function testOrElseWithNoneAsFirst()
90897 {
90898 $a = \PhpOption\None::create();
90899 $b = new \PhpOption\Some('b');
90900
90901 $this->assertEquals('b', $a->orElse($b)->get());
90902 }
90903
90904 public function testOrElseWithLazyOptions()
90905 {
90906 $throws = function() { throw new \LogicException('Should never be called.'); };
90907
90908 $a = new \PhpOption\Some('a');
90909 $b = new \PhpOption\LazyOption($throws);
90910
90911 $this->assertEquals('a', $a->orElse($b)->get());
90912 }
90913
90914 public function testOrElseWithMultipleAlternatives()
90915 {
90916 $throws = new \PhpOption\LazyOption(function() { throw new \LogicException('Should never be called.'); });
90917 $returns = new \PhpOption\LazyOption(function() { return new \PhpOption\Some('foo'); });
90918
90919 $a = \PhpOption\None::create();
90920
90921 $this->assertEquals('foo', $a->orElse($returns)->orElse($throws)->get());
90922 }
90923 }<?php
90924
90925 namespace PhpOption\Tests;
90926
90927 use PhpOption\None;
90928
90929 class NoneTest extends \PHPUnit_Framework_TestCase
90930 {
90931 private $none;
90932
90933 /**
90934 * @expectedException \RuntimeException
90935 */
90936 public function testGet()
90937 {
90938 $none = \PhpOption\None::create();
90939 $none->get();
90940 }
90941
90942 public function testGetOrElse()
90943 {
90944 $none = \PhpOption\None::create();
90945 $this->assertEquals('foo', $none->getOrElse('foo'));
90946 }
90947
90948 public function testGetOrCall()
90949 {
90950 $none = \PhpOption\None::create();
90951 $this->assertEquals('foo', $none->getOrCall(function() { return 'foo'; }));
90952 }
90953
90954 /**
90955 * @expectedException \RuntimeException
90956 * @expectedExceptionMessage Not Found!
90957 */
90958 public function testGetOrThrow()
90959 {
90960 None::create()->getOrThrow(new \RuntimeException('Not Found!'));
90961 }
90962
90963 public function testIsEmpty()
90964 {
90965 $none = \PhpOption\None::create();
90966 $this->assertTrue($none->isEmpty());
90967 }
90968
90969 public function testOrElse()
90970 {
90971 $option = \PhpOption\Some::create('foo');
90972 $this->assertSame($option, \PhpOption\None::create()->orElse($option));
90973 }
90974
90975 public function testifDefined()
90976 {
90977 $this->assertNull($this->none->ifDefined(function() {
90978 throw new \LogicException('Should never be called.');
90979 }));
90980 }
90981
90982 public function testForAll()
90983 {
90984 $this->assertSame($this->none, $this->none->forAll(function() {
90985 throw new \LogicException('Should never be called.');
90986 }));
90987 }
90988
90989 public function testMap()
90990 {
90991 $this->assertSame($this->none, $this->none->map(function() {
90992 throw new \LogicException('Should not be called.');
90993 }));
90994 }
90995
90996 public function testFlatMap()
90997 {
90998 $this->assertSame($this->none, $this->none->flatMap(function() {
90999 throw new \LogicException('Should not be called.');
91000 }));
91001 }
91002
91003 public function testFilter()
91004 {
91005 $this->assertSame($this->none, $this->none->filter(function() {
91006 throw new \LogicException('Should not be called.');
91007 }));
91008 }
91009
91010 public function testFilterNot()
91011 {
91012 $this->assertSame($this->none, $this->none->filterNot(function() {
91013 throw new \LogicException('Should not be called.');
91014 }));
91015 }
91016
91017 public function testSelect()
91018 {
91019 $this->assertSame($this->none, $this->none->select(null));
91020 }
91021
91022 public function testReject()
91023 {
91024 $this->assertSame($this->none, $this->none->reject(null));
91025 }
91026
91027 protected function setUp()
91028 {
91029 $this->none = None::create();
91030 }
91031 }<?php
91032
91033 namespace PhpOption\Tests;
91034
91035 use PhpOption\Some;
91036
91037 class SomeTest extends \PHPUnit_Framework_TestCase
91038 {
91039 public function testGet()
91040 {
91041 $some = new \PhpOption\Some('foo');
91042 $this->assertEquals('foo', $some->get());
91043 $this->assertEquals('foo', $some->getOrElse(null));
91044 $this->assertEquals('foo', $some->getOrCall('does_not_exist'));
91045 $this->assertEquals('foo', $some->getOrThrow(new \RuntimeException('Not found')));
91046 $this->assertFalse($some->isEmpty());
91047 }
91048
91049 public function testCreate()
91050 {
91051 $some = \PhpOption\Some::create('foo');
91052 $this->assertEquals('foo', $some->get());
91053 $this->assertEquals('foo', $some->getOrElse(null));
91054 $this->assertEquals('foo', $some->getOrCall('does_not_exist'));
91055 $this->assertEquals('foo', $some->getOrThrow(new \RuntimeException('Not found')));
91056 $this->assertFalse($some->isEmpty());
91057 }
91058
91059 public function testOrElse()
91060 {
91061 $some = \PhpOption\Some::create('foo');
91062 $this->assertSame($some, $some->orElse(\PhpOption\None::create()));
91063 $this->assertSame($some, $some->orElse(\PhpOption\Some::create('bar')));
91064 }
91065
91066 public function testifDefined()
91067 {
91068 $called = false;
91069 $self = $this;
91070 $some = new Some('foo');
91071 $this->assertNull($some->ifDefined(function($v) use (&$called, $self) {
91072 $called = true;
91073 $self->assertEquals('foo', $v);
91074 }));
91075 $this->assertTrue($called);
91076 }
91077
91078 public function testForAll()
91079 {
91080 $called = false;
91081 $self = $this;
91082 $some = new Some('foo');
91083 $this->assertSame($some, $some->forAll(function($v) use (&$called, $self) {
91084 $called = true;
91085 $self->assertEquals('foo', $v);
91086 }));
91087 $this->assertTrue($called);
91088 }
91089
91090 public function testMap()
91091 {
91092 $some = new Some('foo');
91093 $this->assertEquals('o', $some->map(function($v) { return substr($v, 1, 1); })->get());
91094 }
91095
91096 public function testFlatMap()
91097 {
91098 $repo = new Repository(array('foo'));
91099
91100 $this->assertEquals(array('name' => 'foo'), $repo->getLastRegisteredUsername()
91101 ->flatMap(array($repo, 'getUser'))
91102 ->getOrCall(array($repo, 'getDefaultUser')));
91103 }
91104
91105 public function testFilter()
91106 {
91107 $some = new Some('foo');
91108
91109 $this->assertInstanceOf('PhpOption\None', $some->filter(function($v) { return 0 === strlen($v); }));
91110 $this->assertSame($some, $some->filter(function($v) { return strlen($v) > 0; }));
91111 }
91112
91113 public function testFilterNot()
91114 {
91115 $some = new Some('foo');
91116
91117 $this->assertInstanceOf('PhpOption\None', $some->filterNot(function($v) { return strlen($v) > 0; }));
91118 $this->assertSame($some, $some->filterNot(function($v) { return strlen($v) === 0; }));
91119 }
91120
91121 public function testSelect()
91122 {
91123 $some = new Some('foo');
91124
91125 $this->assertSame($some, $some->select('foo'));
91126 $this->assertInstanceOf('PhpOption\None', $some->select('bar'));
91127 $this->assertInstanceOf('PhpOption\None', $some->select(true));
91128 }
91129
91130 public function testReject()
91131 {
91132 $some = new Some('foo');
91133
91134 $this->assertSame($some, $some->reject(null));
91135 $this->assertSame($some, $some->reject(true));
91136 $this->assertInstanceOf('PhpOption\None', $some->reject('foo'));
91137 }
91138 }
91139
91140 // For the interested reader of these tests, we have gone some great lengths
91141 // to come up with a non-contrived example that might also be used in the
91142 // real-world, and not only for testing purposes :)
91143 class Repository
91144 {
91145 private $users;
91146
91147 public function __construct(array $users = array())
91148 {
91149 $this->users = $users;
91150 }
91151
91152 // A fast ID lookup, probably cached, sometimes we might not need the entire user.
91153 public function getLastRegisteredUsername()
91154 {
91155 if (empty($this->users)) {
91156 return \PhpOption\None::create();
91157 }
91158
91159 return new Some(end($this->users));
91160 }
91161
91162 // Returns a user object (we will live with an array here).
91163 public function getUser($name)
91164 {
91165 if (in_array($name, $this->users, true)) {
91166 return new Some(array('name' => $name));
91167 }
91168
91169 return \PhpOption\None::create();
91170 }
91171
91172 public function getDefaultUser()
91173 {
91174 return array('name' => 'muhuhu');
91175 }
91176 }
91177 <?php
91178
91179 namespace PhpOption\Tests;
91180
91181 /**
91182 * @group performance
91183 */
91184 class PerformanceTest extends \PHPUnit_Framework_TestCase
91185 {
91186 private $traditionalRepo;
91187 private $phpOptionRepo;
91188
91189 public function testSomeCase()
91190 {
91191 $traditionalTime = microtime(true);
91192 for ($i=0; $i<10000; $i++) {
91193 if (null === $rs = $this->traditionalRepo->findMaybe(true)) {
91194 $rs = new \stdClass();
91195 }
91196 }
91197 $traditionalTime = microtime(true) - $traditionalTime;
91198
91199 $phpOptionTime = microtime(true);
91200 for ($i=0; $i<10000; $i++) {
91201 $rs = $this->phpOptionRepo->findMaybe(true)->getOrElse(new \stdClass);
91202 }
91203 $phpOptionTime = microtime(true) - $phpOptionTime;
91204
91205 $overheadPerInvocation = ($phpOptionTime - $traditionalTime) / 10000;
91206 printf("Overhead per invocation (some case): %.9fs\n", $overheadPerInvocation);
91207 }
91208
91209 public function testNoneCase()
91210 {
91211 $traditionalTime = microtime(true);
91212 for ($i=0; $i<10000; $i++) {
91213 if (null === $rs = $this->traditionalRepo->findMaybe(false)) {
91214 $rs = new \stdClass();
91215 }
91216 }
91217 $traditionalTime = microtime(true) - $traditionalTime;
91218
91219 $phpOptionTime = microtime(true);
91220 for ($i=0; $i<10000; $i++) {
91221 $rs = $this->phpOptionRepo->findMaybe(false)->getOrElse(new \stdClass);
91222 }
91223 $phpOptionTime = microtime(true) - $phpOptionTime;
91224
91225 $overheadPerInvocation = ($phpOptionTime - $traditionalTime) / 10000;
91226 printf("Overhead per invocation (none case): %.9fs\n", $overheadPerInvocation);
91227 }
91228
91229 protected function setUp()
91230 {
91231 $this->traditionalRepo = new TraditionalRepo();
91232 $this->phpOptionRepo = new PhpOptionRepo();
91233 }
91234 }
91235
91236 class TraditionalRepo
91237 {
91238 public function findMaybe($success)
91239 {
91240 if ($success) {
91241 return new \stdClass;
91242 }
91243
91244 return null;
91245 }
91246 }
91247
91248 class PhpOptionRepo
91249 {
91250 public function findMaybe($success)
91251 {
91252 if ($success) {
91253 return new \PhpOption\Some(new \stdClass);
91254 }
91255
91256 return \PhpOption\None::create();
91257 }
91258 }<?php
91259
91260 namespace PhpOption\Tests;
91261
91262 use PhpOption\LazyOption;
91263
91264 class LazyOptionTest extends \PHPUnit_Framework_TestCase
91265 {
91266 private $subject;
91267
91268 public function setUp()
91269 {
91270 $this->subject = $this
91271 ->getMockBuilder('Subject')
91272 ->setMethods(array('execute'))
91273 ->getMock();
91274 }
91275
91276 public function testGetWithArgumentsAndConstructor()
91277 {
91278 $some = \PhpOption\LazyOption::create(array($this->subject, 'execute'), array('foo'));
91279
91280 $this->subject
91281 ->expects($this->once())
91282 ->method('execute')
91283 ->with('foo')
91284 ->will($this->returnValue(\PhpOption\Some::create('foo')));
91285
91286 $this->assertEquals('foo', $some->get());
91287 $this->assertEquals('foo', $some->getOrElse(null));
91288 $this->assertEquals('foo', $some->getOrCall('does_not_exist'));
91289 $this->assertEquals('foo', $some->getOrThrow(new \RuntimeException('does_not_exist')));
91290 $this->assertFalse($some->isEmpty());
91291 }
91292
91293 public function testGetWithArgumentsAndCreate()
91294 {
91295 $some = new \PhpOption\LazyOption(array($this->subject, 'execute'), array('foo'));
91296
91297 $this->subject
91298 ->expects($this->once())
91299 ->method('execute')
91300 ->with('foo')
91301 ->will($this->returnValue(\PhpOption\Some::create('foo')));
91302
91303 $this->assertEquals('foo', $some->get());
91304 $this->assertEquals('foo', $some->getOrElse(null));
91305 $this->assertEquals('foo', $some->getOrCall('does_not_exist'));
91306 $this->assertEquals('foo', $some->getOrThrow(new \RuntimeException('does_not_exist')));
91307 $this->assertFalse($some->isEmpty());
91308 }
91309
91310 public function testGetWithoutArgumentsAndConstructor()
91311 {
91312 $some = new \PhpOption\LazyOption(array($this->subject, 'execute'));
91313
91314 $this->subject
91315 ->expects($this->once())
91316 ->method('execute')
91317 ->will($this->returnValue(\PhpOption\Some::create('foo')));
91318
91319 $this->assertEquals('foo', $some->get());
91320 $this->assertEquals('foo', $some->getOrElse(null));
91321 $this->assertEquals('foo', $some->getOrCall('does_not_exist'));
91322 $this->assertEquals('foo', $some->getOrThrow(new \RuntimeException('does_not_exist')));
91323 $this->assertFalse($some->isEmpty());
91324 }
91325
91326 public function testGetWithoutArgumentsAndCreate()
91327 {
91328 $option = \PhpOption\LazyOption::create(array($this->subject, 'execute'));
91329
91330 $this->subject
91331 ->expects($this->once())
91332 ->method('execute')
91333 ->will($this->returnValue(\PhpOption\Some::create('foo')));
91334
91335 $this->assertTrue($option->isDefined());
91336 $this->assertFalse($option->isEmpty());
91337 $this->assertEquals('foo', $option->get());
91338 $this->assertEquals('foo', $option->getOrElse(null));
91339 $this->assertEquals('foo', $option->getOrCall('does_not_exist'));
91340 $this->assertEquals('foo', $option->getOrThrow(new \RuntimeException('does_not_exist')));
91341 }
91342
91343 /**
91344 * @expectedException \RuntimeException
91345 * @expectedExceptionMessage None has no value
91346 */
91347 public function testCallbackReturnsNull()
91348 {
91349 $option = \PhpOption\LazyOption::create(array($this->subject, 'execute'));
91350
91351 $this->subject
91352 ->expects($this->once())
91353 ->method('execute')
91354 ->will($this->returnValue(\PhpOption\None::create()));
91355
91356 $this->assertFalse($option->isDefined());
91357 $this->assertTrue($option->isEmpty());
91358 $this->assertEquals('alt', $option->getOrElse('alt'));
91359 $this->assertEquals('alt', $option->getOrCall(function(){return 'alt';}));
91360
91361 $option->get();
91362 }
91363
91364 /**
91365 * @expectedException \RuntimeException
91366 * @expectedExceptionMessage Expected instance of \PhpOption\Option
91367 */
91368 public function testExceptionIsThrownIfCallbackReturnsNonOption()
91369 {
91370 $option = \PhpOption\LazyOption::create(array($this->subject, 'execute'));
91371
91372 $this->subject
91373 ->expects($this->once())
91374 ->method('execute')
91375 ->will($this->returnValue(null));
91376
91377 $this->assertFalse($option->isDefined());
91378 }
91379
91380 /**
91381 * @expectedException InvalidArgumentException
91382 * @expectedExceptionMessage Invalid callback given
91383 */
91384 public function testInvalidCallbackAndConstructor()
91385 {
91386 new \PhpOption\LazyOption('invalidCallback');
91387 }
91388
91389 /**
91390 * @expectedException InvalidArgumentException
91391 * @expectedExceptionMessage Invalid callback given
91392 */
91393 public function testInvalidCallbackAndCreate()
91394 {
91395 \PhpOption\LazyOption::create('invalidCallback');
91396 }
91397
91398 public function testifDefined()
91399 {
91400 $called = false;
91401 $self = $this;
91402 $this->assertNull(LazyOption::fromValue('foo')->ifDefined(function($v) use (&$called, $self) {
91403 $called = true;
91404 $self->assertEquals('foo', $v);
91405 }));
91406 $this->assertTrue($called);
91407 }
91408
91409 public function testForAll()
91410 {
91411 $called = false;
91412 $self = $this;
91413 $this->assertInstanceOf('PhpOption\Some', LazyOption::fromValue('foo')->forAll(function($v) use (&$called, $self) {
91414 $called = true;
91415 $self->assertEquals('foo', $v);
91416 }));
91417 $this->assertTrue($called);
91418 }
91419
91420 public function testOrElse()
91421 {
91422 $some = \PhpOption\Some::create('foo');
91423 $lazy = \PhpOption\LazyOption::create(function() use ($some) {return $some;});
91424 $this->assertSame($some, $lazy->orElse(\PhpOption\None::create()));
91425 $this->assertSame($some, $lazy->orElse(\PhpOption\Some::create('bar')));
91426 }
91427 }
91428 {
91429 "name": "phpoption/phpoption",
91430 "description": "Option Type for PHP",
91431 "keywords": ["php","option","language","type"],
91432 "type": "library",
91433 "license": "Apache2",
91434 "authors": [
91435 {
91436 "name": "Johannes M. Schmitt",
91437 "email": "schmittjoh@gmail.com"
91438 }
91439 ],
91440 "require": {
91441 "php": ">=5.3.0"
91442 },
91443 "autoload": {
91444 "psr-0": { "PhpOption\\": "src/" }
91445 },
91446 "extra": {
91447 "branch-alias": {
91448 "dev-master": "1.3-dev"
91449 }
91450 }
91451 }
91452 <?xml version="1.0" encoding="UTF-8"?>
91453
91454 <phpunit backupGlobals="false"
91455 backupStaticAttributes="false"
91456 colors="true"
91457 convertErrorsToExceptions="true"
91458 convertNoticesToExceptions="true"
91459 convertWarningsToExceptions="true"
91460 processIsolation="false"
91461 stopOnFailure="false"
91462 syntaxCheck="false"
91463 bootstrap="tests/bootstrap.php"
91464 >
91465 <testsuites>
91466 <testsuite name="PhpOption Type Test Suite">
91467 <directory>./tests/PhpOption/</directory>
91468 </testsuite>
91469 </testsuites>
91470
91471 <groups>
91472 <exclude>
91473 <group>performance</group>
91474 </exclude>
91475 </groups>
91476 </phpunit>
91477 Apache License
91478 Version 2.0, January 2004
91479 http://www.apache.org/licenses/
91480
91481 TERMS AND CONDITIONS FOR USE, REPRODUCTION, AND DISTRIBUTION
91482
91483 1. Definitions.
91484
91485 "License" shall mean the terms and conditions for use, reproduction,
91486 and distribution as defined by Sections 1 through 9 of this document.
91487
91488 "Licensor" shall mean the copyright owner or entity authorized by
91489 the copyright owner that is granting the License.
91490
91491 "Legal Entity" shall mean the union of the acting entity and all
91492 other entities that control, are controlled by, or are under common
91493 control with that entity. For the purposes of this definition,
91494 "control" means (i) the power, direct or indirect, to cause the
91495 direction or management of such entity, whether by contract or
91496 otherwise, or (ii) ownership of fifty percent (50%) or more of the
91497 outstanding shares, or (iii) beneficial ownership of such entity.
91498
91499 "You" (or "Your") shall mean an individual or Legal Entity
91500 exercising permissions granted by this License.
91501
91502 "Source" form shall mean the preferred form for making modifications,
91503 including but not limited to software source code, documentation
91504 source, and configuration files.
91505
91506 "Object" form shall mean any form resulting from mechanical
91507 transformation or translation of a Source form, including but
91508 not limited to compiled object code, generated documentation,
91509 and conversions to other media types.
91510
91511 "Work" shall mean the work of authorship, whether in Source or
91512 Object form, made available under the License, as indicated by a
91513 copyright notice that is included in or attached to the work
91514 (an example is provided in the Appendix below).
91515
91516 "Derivative Works" shall mean any work, whether in Source or Object
91517 form, that is based on (or derived from) the Work and for which the
91518 editorial revisions, annotations, elaborations, or other modifications
91519 represent, as a whole, an original work of authorship. For the purposes
91520 of this License, Derivative Works shall not include works that remain
91521 separable from, or merely link (or bind by name) to the interfaces of,
91522 the Work and Derivative Works thereof.
91523
91524 "Contribution" shall mean any work of authorship, including
91525 the original version of the Work and any modifications or additions
91526 to that Work or Derivative Works thereof, that is intentionally
91527 submitted to Licensor for inclusion in the Work by the copyright owner
91528 or by an individual or Legal Entity authorized to submit on behalf of
91529 the copyright owner. For the purposes of this definition, "submitted"
91530 means any form of electronic, verbal, or written communication sent
91531 to the Licensor or its representatives, including but not limited to
91532 communication on electronic mailing lists, source code control systems,
91533 and issue tracking systems that are managed by, or on behalf of, the
91534 Licensor for the purpose of discussing and improving the Work, but
91535 excluding communication that is conspicuously marked or otherwise
91536 designated in writing by the copyright owner as "Not a Contribution."
91537
91538 "Contributor" shall mean Licensor and any individual or Legal Entity
91539 on behalf of whom a Contribution has been received by Licensor and
91540 subsequently incorporated within the Work.
91541
91542 2. Grant of Copyright License. Subject to the terms and conditions of
91543 this License, each Contributor hereby grants to You a perpetual,
91544 worldwide, non-exclusive, no-charge, royalty-free, irrevocable
91545 copyright license to reproduce, prepare Derivative Works of,
91546 publicly display, publicly perform, sublicense, and distribute the
91547 Work and such Derivative Works in Source or Object form.
91548
91549 3. Grant of Patent License. Subject to the terms and conditions of
91550 this License, each Contributor hereby grants to You a perpetual,
91551 worldwide, non-exclusive, no-charge, royalty-free, irrevocable
91552 (except as stated in this section) patent license to make, have made,
91553 use, offer to sell, sell, import, and otherwise transfer the Work,
91554 where such license applies only to those patent claims licensable
91555 by such Contributor that are necessarily infringed by their
91556 Contribution(s) alone or by combination of their Contribution(s)
91557 with the Work to which such Contribution(s) was submitted. If You
91558 institute patent litigation against any entity (including a
91559 cross-claim or counterclaim in a lawsuit) alleging that the Work
91560 or a Contribution incorporated within the Work constitutes direct
91561 or contributory patent infringement, then any patent licenses
91562 granted to You under this License for that Work shall terminate
91563 as of the date such litigation is filed.
91564
91565 4. Redistribution. You may reproduce and distribute copies of the
91566 Work or Derivative Works thereof in any medium, with or without
91567 modifications, and in Source or Object form, provided that You
91568 meet the following conditions:
91569
91570 (a) You must give any other recipients of the Work or
91571 Derivative Works a copy of this License; and
91572
91573 (b) You must cause any modified files to carry prominent notices
91574 stating that You changed the files; and
91575
91576 (c) You must retain, in the Source form of any Derivative Works
91577 that You distribute, all copyright, patent, trademark, and
91578 attribution notices from the Source form of the Work,
91579 excluding those notices that do not pertain to any part of
91580 the Derivative Works; and
91581
91582 (d) If the Work includes a "NOTICE" text file as part of its
91583 distribution, then any Derivative Works that You distribute must
91584 include a readable copy of the attribution notices contained
91585 within such NOTICE file, excluding those notices that do not
91586 pertain to any part of the Derivative Works, in at least one
91587 of the following places: within a NOTICE text file distributed
91588 as part of the Derivative Works; within the Source form or
91589 documentation, if provided along with the Derivative Works; or,
91590 within a display generated by the Derivative Works, if and
91591 wherever such third-party notices normally appear. The contents
91592 of the NOTICE file are for informational purposes only and
91593 do not modify the License. You may add Your own attribution
91594 notices within Derivative Works that You distribute, alongside
91595 or as an addendum to the NOTICE text from the Work, provided
91596 that such additional attribution notices cannot be construed
91597 as modifying the License.
91598
91599 You may add Your own copyright statement to Your modifications and
91600 may provide additional or different license terms and conditions
91601 for use, reproduction, or distribution of Your modifications, or
91602 for any such Derivative Works as a whole, provided Your use,
91603 reproduction, and distribution of the Work otherwise complies with
91604 the conditions stated in this License.
91605
91606 5. Submission of Contributions. Unless You explicitly state otherwise,
91607 any Contribution intentionally submitted for inclusion in the Work
91608 by You to the Licensor shall be under the terms and conditions of
91609 this License, without any additional terms or conditions.
91610 Notwithstanding the above, nothing herein shall supersede or modify
91611 the terms of any separate license agreement you may have executed
91612 with Licensor regarding such Contributions.
91613
91614 6. Trademarks. This License does not grant permission to use the trade
91615 names, trademarks, service marks, or product names of the Licensor,
91616 except as required for reasonable and customary use in describing the
91617 origin of the Work and reproducing the content of the NOTICE file.
91618
91619 7. Disclaimer of Warranty. Unless required by applicable law or
91620 agreed to in writing, Licensor provides the Work (and each
91621 Contributor provides its Contributions) on an "AS IS" BASIS,
91622 WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or
91623 implied, including, without limitation, any warranties or conditions
91624 of TITLE, NON-INFRINGEMENT, MERCHANTABILITY, or FITNESS FOR A
91625 PARTICULAR PURPOSE. You are solely responsible for determining the
91626 appropriateness of using or redistributing the Work and assume any
91627 risks associated with Your exercise of permissions under this License.
91628
91629 8. Limitation of Liability. In no event and under no legal theory,
91630 whether in tort (including negligence), contract, or otherwise,
91631 unless required by applicable law (such as deliberate and grossly
91632 negligent acts) or agreed to in writing, shall any Contributor be
91633 liable to You for damages, including any direct, indirect, special,
91634 incidental, or consequential damages of any character arising as a
91635 result of this License or out of the use or inability to use the
91636 Work (including but not limited to damages for loss of goodwill,
91637 work stoppage, computer failure or malfunction, or any and all
91638 other commercial damages or losses), even if such Contributor
91639 has been advised of the possibility of such damages.
91640
91641 9. Accepting Warranty or Additional Liability. While redistributing
91642 the Work or Derivative Works thereof, You may choose to offer,
91643 and charge a fee for, acceptance of support, warranty, indemnity,
91644 or other liability obligations and/or rights consistent with this
91645 License. However, in accepting such obligations, You may act only
91646 on Your own behalf and on Your sole responsibility, not on behalf
91647 of any other Contributor, and only if You agree to indemnify,
91648 defend, and hold each Contributor harmless for any liability
91649 incurred by, or claims asserted against, such Contributor by reason
91650 of your accepting any such warranty or additional liability.
91651
91652 END OF TERMS AND CONDITIONS
91653
91654 APPENDIX: How to apply the Apache License to your work.
91655
91656 To apply the Apache License to your work, attach the following
91657 boilerplate notice, with the fields enclosed by brackets "[]"
91658 replaced with your own identifying information. (Don't include
91659 the brackets!) The text should be enclosed in the appropriate
91660 comment syntax for the file format. We also recommend that a
91661 file or class name and description of purpose be included on the
91662 same "printed page" as the copyright notice for easier
91663 identification within third-party archives.
91664
91665 Copyright [yyyy] [name of copyright owner]
91666
91667 Licensed under the Apache License, Version 2.0 (the "License");
91668 you may not use this file except in compliance with the License.
91669 You may obtain a copy of the License at
91670
91671 http://www.apache.org/licenses/LICENSE-2.0
91672
91673 Unless required by applicable law or agreed to in writing, software
91674 distributed under the License is distributed on an "AS IS" BASIS,
91675 WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
91676 See the License for the specific language governing permissions and
91677 limitations under the License.<?php
91678
91679 /*
91680 * Copyright 2011 Johannes M. Schmitt <schmittjoh@gmail.com>
91681 *
91682 * Licensed under the Apache License, Version 2.0 (the "License");
91683 * you may not use this file except in compliance with the License.
91684 * You may obtain a copy of the License at
91685 *
91686 * http://www.apache.org/licenses/LICENSE-2.0
91687 *
91688 * Unless required by applicable law or agreed to in writing, software
91689 * distributed under the License is distributed on an "AS IS" BASIS,
91690 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
91691 * See the License for the specific language governing permissions and
91692 * limitations under the License.
91693 */
91694
91695 namespace Metadata;
91696
91697 final class Version
91698 {
91699 const VERSION = '1.4-DEV';
91700 }
91701 <?php
91702
91703 /*
91704 * Copyright 2011 Johannes M. Schmitt <schmittjoh@gmail.com>
91705 *
91706 * Licensed under the Apache License, Version 2.0 (the "License");
91707 * you may not use this file except in compliance with the License.
91708 * You may obtain a copy of the License at
91709 *
91710 * http://www.apache.org/licenses/LICENSE-2.0
91711 *
91712 * Unless required by applicable law or agreed to in writing, software
91713 * distributed under the License is distributed on an "AS IS" BASIS,
91714 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
91715 * See the License for the specific language governing permissions and
91716 * limitations under the License.
91717 */
91718
91719 namespace Metadata\Driver;
91720
91721 final class DriverChain implements AdvancedDriverInterface
91722 {
91723 private $drivers;
91724
91725 public function __construct(array $drivers)
91726 {
91727 $this->drivers = $drivers;
91728 }
91729
91730 public function loadMetadataForClass(\ReflectionClass $class)
91731 {
91732 foreach ($this->drivers as $driver) {
91733 if (null !== $metadata = $driver->loadMetadataForClass($class)) {
91734 return $metadata;
91735 }
91736 }
91737
91738 return null;
91739 }
91740
91741 /**
91742 * {@inheritDoc}
91743 */
91744 public function getAllClassNames()
91745 {
91746 $classes = array();
91747 foreach ($this->drivers as $driver) {
91748 if (!$driver instanceof AdvancedDriverInterface) {
91749 throw new \RuntimeException(
91750 sprintf(
91751 'Driver "%s" must be an instance of "AdvancedDriverInterface" to use '.
91752 '"DriverChain::getAllClassNames()".',
91753 get_class($driver)
91754 )
91755 );
91756 }
91757 $driverClasses = $driver->getAllClassNames();
91758 if (!empty($driverClasses)) {
91759 $classes = array_merge($classes, $driverClasses);
91760 }
91761 }
91762
91763 return $classes;
91764 }
91765 }
91766 <?php
91767
91768 namespace Metadata\Driver;
91769
91770 class FileLocator implements AdvancedFileLocatorInterface
91771 {
91772 private $dirs;
91773
91774 public function __construct(array $dirs)
91775 {
91776 $this->dirs = $dirs;
91777 }
91778
91779 public function getDirs()
91780 {
91781 return $this->dirs;
91782 }
91783
91784 /**
91785 * @param \ReflectionClass $class
91786 * @param string $extension
91787 *
91788 * @return string|null
91789 */
91790 public function findFileForClass(\ReflectionClass $class, $extension)
91791 {
91792 foreach ($this->dirs as $prefix => $dir) {
91793 if ('' !== $prefix && 0 !== strpos($class->getNamespaceName(), $prefix)) {
91794 continue;
91795 }
91796
91797 $len = '' === $prefix ? 0 : strlen($prefix) + 1;
91798 $path = $dir.'/'.str_replace('\\', '.', substr($class->name, $len)).'.'.$extension;
91799 if (file_exists($path)) {
91800 return $path;
91801 }
91802 }
91803
91804 return null;
91805 }
91806
91807 /**
91808 * {@inheritDoc}
91809 */
91810 public function findAllClasses($extension)
91811 {
91812 $classes = array();
91813 foreach ($this->dirs as $prefix => $dir) {
91814 /** @var $iterator \RecursiveIteratorIterator|\SplFileInfo[] */
91815 $iterator = new \RecursiveIteratorIterator(
91816 new \RecursiveDirectoryIterator($dir),
91817 \RecursiveIteratorIterator::LEAVES_ONLY
91818 );
91819 $nsPrefix = $prefix !== '' ? $prefix.'\\' : '';
91820 foreach ($iterator as $file) {
91821 if (($fileName = $file->getBasename('.'.$extension)) == $file->getBasename()) {
91822 continue;
91823 }
91824
91825 $classes[] = $nsPrefix.str_replace('.', '\\', $fileName);
91826 }
91827 }
91828
91829 return $classes;
91830 }
91831 }
91832 <?php
91833
91834 /*
91835 * Copyright 2011 Johannes M. Schmitt <schmittjoh@gmail.com>
91836 *
91837 * Licensed under the Apache License, Version 2.0 (the "License");
91838 * you may not use this file except in compliance with the License.
91839 * You may obtain a copy of the License at
91840 *
91841 * http://www.apache.org/licenses/LICENSE-2.0
91842 *
91843 * Unless required by applicable law or agreed to in writing, software
91844 * distributed under the License is distributed on an "AS IS" BASIS,
91845 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
91846 * See the License for the specific language governing permissions and
91847 * limitations under the License.
91848 */
91849
91850 namespace Metadata\Driver;
91851
91852 /**
91853 * Forces advanced logic on a file locator.
91854 *
91855 * @author Jordan Stout <j@jrdn.org>
91856 */
91857 interface AdvancedFileLocatorInterface extends FileLocatorInterface
91858 {
91859 /**
91860 * Finds all possible metadata files.
91861 *
91862 * @param string $extension
91863 *
91864 * @return array
91865 */
91866 public function findAllClasses($extension);
91867 }<?php
91868
91869 /*
91870 * Copyright 2011 Johannes M. Schmitt <schmittjoh@gmail.com>
91871 *
91872 * Licensed under the Apache License, Version 2.0 (the "License");
91873 * you may not use this file except in compliance with the License.
91874 * You may obtain a copy of the License at
91875 *
91876 * http://www.apache.org/licenses/LICENSE-2.0
91877 *
91878 * Unless required by applicable law or agreed to in writing, software
91879 * distributed under the License is distributed on an "AS IS" BASIS,
91880 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
91881 * See the License for the specific language governing permissions and
91882 * limitations under the License.
91883 */
91884
91885 namespace Metadata\Driver;
91886
91887 /**
91888 * Forces advanced logic to drivers.
91889 *
91890 * @author Jordan Stout <j@jrdn.org>
91891 */
91892 interface AdvancedDriverInterface extends DriverInterface
91893 {
91894 /**
91895 * Gets all the metadata class names known to this driver.
91896 *
91897 * @return array
91898 */
91899 public function getAllClassNames();
91900 }<?php
91901
91902 namespace Metadata\Driver;
91903
91904 interface FileLocatorInterface
91905 {
91906 /**
91907 * @param \ReflectionClass $class
91908 * @param string $extension
91909 *
91910 * @return string|null
91911 */
91912 public function findFileForClass(\ReflectionClass $class, $extension);
91913 }
91914 <?php
91915
91916 namespace Metadata\Driver;
91917
91918 use Symfony\Component\DependencyInjection\ContainerInterface;
91919
91920 class LazyLoadingDriver implements DriverInterface
91921 {
91922 private $container;
91923 private $realDriverId;
91924
91925 public function __construct(ContainerInterface $container, $realDriverId)
91926 {
91927 $this->container = $container;
91928 $this->realDriverId = $realDriverId;
91929 }
91930
91931 /**
91932 * {@inheritDoc}
91933 */
91934 public function loadMetadataForClass(\ReflectionClass $class)
91935 {
91936 return $this->container->get($this->realDriverId)->loadMetadataForClass($class);
91937 }
91938 }
91939 <?php
91940
91941 namespace Metadata\Driver;
91942
91943 /**
91944 * Base file driver implementation.
91945 *
91946 * @author Johannes M. Schmitt <schmittjoh@gmail.com>
91947 */
91948 abstract class AbstractFileDriver implements AdvancedDriverInterface
91949 {
91950 /**
91951 * @var FileLocatorInterface|FileLocator
91952 */
91953 private $locator;
91954
91955 public function __construct(FileLocatorInterface $locator)
91956 {
91957 $this->locator = $locator;
91958 }
91959
91960 public function loadMetadataForClass(\ReflectionClass $class)
91961 {
91962 if (null === $path = $this->locator->findFileForClass($class, $this->getExtension())) {
91963 return null;
91964 }
91965
91966 return $this->loadMetadataFromFile($class, $path);
91967 }
91968
91969 /**
91970 * {@inheritDoc}
91971 */
91972 public function getAllClassNames()
91973 {
91974 if (!$this->locator instanceof AdvancedFileLocatorInterface) {
91975 throw new \RuntimeException('Locator "%s" must be an instance of "AdvancedFileLocatorInterface".');
91976 }
91977
91978 return $this->locator->findAllClasses($this->getExtension());
91979 }
91980
91981 /**
91982 * Parses the content of the file, and converts it to the desired metadata.
91983 *
91984 * @param \ReflectionClass $class
91985 * @param string $file
91986 *
91987 * @return \MetaData\ClassMetadata|null
91988 */
91989 abstract protected function loadMetadataFromFile(\ReflectionClass $class, $file);
91990
91991 /**
91992 * Returns the extension of the file.
91993 *
91994 * @return string
91995 */
91996 abstract protected function getExtension();
91997 }
91998 <?php
91999
92000 /*
92001 * Copyright 2011 Johannes M. Schmitt <schmittjoh@gmail.com>
92002 *
92003 * Licensed under the Apache License, Version 2.0 (the "License");
92004 * you may not use this file except in compliance with the License.
92005 * You may obtain a copy of the License at
92006 *
92007 * http://www.apache.org/licenses/LICENSE-2.0
92008 *
92009 * Unless required by applicable law or agreed to in writing, software
92010 * distributed under the License is distributed on an "AS IS" BASIS,
92011 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
92012 * See the License for the specific language governing permissions and
92013 * limitations under the License.
92014 */
92015
92016 namespace Metadata\Driver;
92017
92018 interface DriverInterface
92019 {
92020 /**
92021 * @param \ReflectionClass $class
92022 *
92023 * @return \Metadata\ClassMetadata
92024 */
92025 public function loadMetadataForClass(\ReflectionClass $class);
92026 }
92027 <?php
92028
92029 /*
92030 * Copyright 2011 Johannes M. Schmitt <schmittjoh@gmail.com>
92031 *
92032 * Licensed under the Apache License, Version 2.0 (the "License");
92033 * you may not use this file except in compliance with the License.
92034 * You may obtain a copy of the License at
92035 *
92036 * http://www.apache.org/licenses/LICENSE-2.0
92037 *
92038 * Unless required by applicable law or agreed to in writing, software
92039 * distributed under the License is distributed on an "AS IS" BASIS,
92040 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
92041 * See the License for the specific language governing permissions and
92042 * limitations under the License.
92043 */
92044
92045 namespace Metadata;
92046
92047 use Metadata\Driver\AdvancedDriverInterface;
92048 use Metadata\Driver\DriverInterface;
92049 use Metadata\Cache\CacheInterface;
92050
92051 final class MetadataFactory implements AdvancedMetadataFactoryInterface
92052 {
92053 private $driver;
92054 private $cache;
92055 private $loadedMetadata = array();
92056 private $loadedClassMetadata = array();
92057 private $hierarchyMetadataClass;
92058 private $includeInterfaces = false;
92059 private $debug;
92060
92061 /**
92062 * @param DriverInterface $driver
92063 * @param string $hierarchyMetadataClass
92064 * @param boolean $debug
92065 */
92066 public function __construct(DriverInterface $driver, $hierarchyMetadataClass = 'Metadata\ClassHierarchyMetadata', $debug = false)
92067 {
92068 $this->driver = $driver;
92069 $this->hierarchyMetadataClass = $hierarchyMetadataClass;
92070 $this->debug = (Boolean) $debug;
92071 }
92072
92073 /**
92074 * @param boolean $bool
92075 */
92076 public function setIncludeInterfaces($include)
92077 {
92078 $this->includeInterfaces = (Boolean) $include;
92079 }
92080
92081 public function setCache(CacheInterface $cache)
92082 {
92083 $this->cache = $cache;
92084 }
92085
92086 /**
92087 * @param string $className
92088 *
92089 * @return ClassMetaData
92090 */
92091 public function getMetadataForClass($className)
92092 {
92093 if (isset($this->loadedMetadata[$className])) {
92094 return $this->loadedMetadata[$className];
92095 }
92096
92097 $metadata = null;
92098 foreach ($this->getClassHierarchy($className) as $class) {
92099 if (isset($this->loadedClassMetadata[$name = $class->getName()])) {
92100 $this->addClassMetadata($metadata, $this->loadedClassMetadata[$name]);
92101 continue;
92102 }
92103
92104 // check the cache
92105 if (null !== $this->cache && (null !== $classMetadata = $this->cache->loadClassMetadataFromCache($class))) {
92106 if ( ! $classMetadata instanceof ClassMetadata) {
92107 throw new \LogicException(sprintf('The cache must return instances of ClassMetadata, but got %s.', var_export($classMetadata, true)));
92108 }
92109
92110 if ($this->debug && !$classMetadata->isFresh()) {
92111 $this->cache->evictClassMetadataFromCache($classMetadata->reflection);
92112 } else {
92113 $this->loadedClassMetadata[$name] = $classMetadata;
92114 $this->addClassMetadata($metadata, $classMetadata);
92115 continue;
92116 }
92117 }
92118
92119 // load from source
92120 if (null !== $classMetadata = $this->driver->loadMetadataForClass($class)) {
92121 $this->loadedClassMetadata[$name] = $classMetadata;
92122 $this->addClassMetadata($metadata, $classMetadata);
92123
92124 if (null !== $this->cache) {
92125 $this->cache->putClassMetadataInCache($classMetadata);
92126 }
92127
92128 continue;
92129 }
92130 }
92131
92132 return $this->loadedMetadata[$className] = $metadata;
92133 }
92134
92135 /**
92136 * {@inheritDoc}
92137 */
92138 public function getAllClassNames()
92139 {
92140 if (!$this->driver instanceof AdvancedDriverInterface) {
92141 throw new \RuntimeException(
92142 sprintf('Driver "%s" must be an instance of "AdvancedDriverInterface".', get_class($this->driver))
92143 );
92144 }
92145
92146 return $this->driver->getAllClassNames();
92147 }
92148
92149 /**
92150 * @param ClassMetadata|null $metadata
92151 * @param ClassMetadata $toAdd
92152 */
92153 private function addClassMetadata(&$metadata, $toAdd)
92154 {
92155 if ($toAdd instanceof MergeableInterface) {
92156 if (null === $metadata) {
92157 $metadata = clone $toAdd;
92158 } else {
92159 $metadata->merge($toAdd);
92160 }
92161 } else {
92162 if (null === $metadata) {
92163 $metadata = new $this->hierarchyMetadataClass;
92164 }
92165
92166 $metadata->addClassMetadata($toAdd);
92167 }
92168 }
92169
92170 private function getClassHierarchy($class)
92171 {
92172 $classes = array();
92173 $refl = new \ReflectionClass($class);
92174
92175 do {
92176 $classes[] = $refl;
92177 $refl = $refl->getParentClass();
92178 } while (false !== $refl);
92179
92180 $classes = array_reverse($classes, false);
92181
92182 if (!$this->includeInterfaces) {
92183 return $classes;
92184 }
92185
92186 $addedInterfaces = array();
92187 $newHierarchy = array();
92188
92189 foreach ($classes as $class) {
92190 foreach ($class->getInterfaces() as $interface) {
92191 if (isset($addedInterfaces[$interface->getName()])) {
92192 continue;
92193 }
92194 $addedInterfaces[$interface->getName()] = true;
92195
92196 $newHierarchy[] = $interface;
92197 }
92198
92199 $newHierarchy[] = $class;
92200 }
92201
92202 return $newHierarchy;
92203 }
92204 }
92205 <?php
92206
92207 namespace Metadata\Cache;
92208
92209 use Metadata\ClassMetadata;
92210
92211 class FileCache implements CacheInterface
92212 {
92213 private $dir;
92214
92215 public function __construct($dir)
92216 {
92217 if (!is_dir($dir)) {
92218 throw new \InvalidArgumentException(sprintf('The directory "%s" does not exist.', $dir));
92219 }
92220 if (!is_writable($dir)) {
92221 throw new \InvalidArgumentException(sprintf('The directory "%s" is not writable.', $dir));
92222 }
92223
92224 $this->dir = rtrim($dir, '\\/');
92225 }
92226
92227 /**
92228 * {@inheritDoc}
92229 */
92230 public function loadClassMetadataFromCache(\ReflectionClass $class)
92231 {
92232 $path = $this->dir.'/'.strtr($class->name, '\\', '-').'.cache.php';
92233 if (!file_exists($path)) {
92234 return null;
92235 }
92236
92237 return include $path;
92238 }
92239
92240 /**
92241 * {@inheritDoc}
92242 */
92243 public function putClassMetadataInCache(ClassMetadata $metadata)
92244 {
92245 $path = $this->dir.'/'.strtr($metadata->name, '\\', '-').'.cache.php';
92246
92247 $tmpFile = tempnam($this->dir, 'metadata-cache');
92248 file_put_contents($tmpFile, '<?php return unserialize('.var_export(serialize($metadata), true).');');
92249 chmod($tmpFile, 0666 & ~umask());
92250
92251 if (false === @rename($tmpFile, $path)) {
92252 throw new \RuntimeException(sprintf('Could not write new cache file to %s.', $path));
92253 }
92254 }
92255
92256 /**
92257 * {@inheritDoc}
92258 */
92259 public function evictClassMetadataFromCache(\ReflectionClass $class)
92260 {
92261 $path = $this->dir.'/'.strtr($class->name, '\\', '-').'.cache.php';
92262 if (file_exists($path)) {
92263 unlink($path);
92264 }
92265 }
92266 }
92267 <?php
92268
92269 namespace Metadata\Cache;
92270
92271 use Metadata\ClassMetadata;
92272
92273 interface CacheInterface
92274 {
92275 /**
92276 * Loads a class metadata instance from the cache
92277 *
92278 * @param \ReflectionClass $class
92279 *
92280 * @return ClassMetadata
92281 */
92282 function loadClassMetadataFromCache(\ReflectionClass $class);
92283
92284 /**
92285 * Puts a class metadata instance into the cache
92286 *
92287 * @param ClassMetadata $metadata
92288 *
92289 * @return void
92290 */
92291 function putClassMetadataInCache(ClassMetadata $metadata);
92292
92293 /**
92294 * Evicts the class metadata for the given class from the cache.
92295 *
92296 * @param \ReflectionClass $class
92297 *
92298 * @return void
92299 */
92300 function evictClassMetadataFromCache(\ReflectionClass $class);
92301 }<?php
92302
92303 namespace Metadata\Cache;
92304
92305 use Doctrine\Common\Cache\Cache;
92306 use Metadata\ClassMetadata;
92307
92308 /**
92309 * @author Henrik Bjornskov <henrik@bjrnskov.dk>
92310 */
92311 class DoctrineCacheAdapter implements CacheInterface
92312 {
92313 /**
92314 * @param string $prefix
92315 */
92316 private $prefix;
92317
92318 /**
92319 * @var Cache $cache
92320 */
92321 private $cache;
92322
92323 /**
92324 * @param string $prefix
92325 * @param Cache $cache
92326 */
92327 public function __construct($prefix, Cache $cache)
92328 {
92329 $this->prefix = $prefix;
92330 $this->cache = $cache;
92331 }
92332
92333 /**
92334 * {@inheritDoc}
92335 */
92336 public function loadClassMetadataFromCache(\ReflectionClass $class)
92337 {
92338 $cache = $this->cache->fetch($this->prefix . $class->name);
92339 return false === $cache ? null : $cache;
92340 }
92341
92342 /**
92343 * {@inheritDoc}
92344 */
92345 public function putClassMetadataInCache(ClassMetadata $metadata)
92346 {
92347 $this->cache->save($this->prefix . $metadata->name, $metadata);
92348 }
92349
92350 /**
92351 * {@inheritDoc}
92352 */
92353 public function evictClassMetadataFromCache(\ReflectionClass $class)
92354 {
92355 $this->cache->delete($this->prefix . $class->name);
92356 }
92357 }
92358 <?php
92359
92360 /*
92361 * Copyright 2011 Johannes M. Schmitt <schmittjoh@gmail.com>
92362 *
92363 * Licensed under the Apache License, Version 2.0 (the "License");
92364 * you may not use this file except in compliance with the License.
92365 * You may obtain a copy of the License at
92366 *
92367 * http://www.apache.org/licenses/LICENSE-2.0
92368 *
92369 * Unless required by applicable law or agreed to in writing, software
92370 * distributed under the License is distributed on an "AS IS" BASIS,
92371 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
92372 * See the License for the specific language governing permissions and
92373 * limitations under the License.
92374 */
92375
92376 namespace Metadata;
92377
92378 /**
92379 * Base class for property metadata.
92380 *
92381 * This class is intended to be extended to add your application specific
92382 * properties, and flags.
92383 *
92384 * @author Johannes M. Schmitt <schmittjoh@gmail.com>
92385 */
92386 class PropertyMetadata implements \Serializable
92387 {
92388 public $class;
92389 public $name;
92390 public $reflection;
92391
92392 public function __construct($class, $name)
92393 {
92394 $this->class = $class;
92395 $this->name = $name;
92396
92397 $this->reflection = new \ReflectionProperty($class, $name);
92398 $this->reflection->setAccessible(true);
92399 }
92400
92401 /**
92402 * @param object $obj
92403 *
92404 * @return mixed
92405 */
92406 public function getValue($obj)
92407 {
92408 return $this->reflection->getValue($obj);
92409 }
92410
92411 /**
92412 * @param object $obj
92413 * @param string $value
92414 */
92415 public function setValue($obj, $value)
92416 {
92417 $this->reflection->setValue($obj, $value);
92418 }
92419
92420 public function serialize()
92421 {
92422 return serialize(array(
92423 $this->class,
92424 $this->name,
92425 ));
92426 }
92427
92428 public function unserialize($str)
92429 {
92430 list($this->class, $this->name) = unserialize($str);
92431
92432 $this->reflection = new \ReflectionProperty($this->class, $this->name);
92433 $this->reflection->setAccessible(true);
92434 }
92435 }
92436 <?php
92437
92438 /*
92439 * Copyright 2011 Johannes M. Schmitt <schmittjoh@gmail.com>
92440 *
92441 * Licensed under the Apache License, Version 2.0 (the "License");
92442 * you may not use this file except in compliance with the License.
92443 * You may obtain a copy of the License at
92444 *
92445 * http://www.apache.org/licenses/LICENSE-2.0
92446 *
92447 * Unless required by applicable law or agreed to in writing, software
92448 * distributed under the License is distributed on an "AS IS" BASIS,
92449 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
92450 * See the License for the specific language governing permissions and
92451 * limitations under the License.
92452 */
92453
92454 namespace Metadata;
92455
92456 /**
92457 * Interface for advanced Metadata Factory implementations.
92458 *
92459 * @author Johannes M. Schmitt <schmittjoh@gmail.com>
92460 * @author Jordan Stout <j@jrdn.org>
92461 */
92462 interface AdvancedMetadataFactoryInterface extends MetadataFactoryInterface
92463 {
92464 /**
92465 * Gets all the possible classes.
92466 *
92467 * @throws \RuntimeException if driver does not an advanced driver.
92468 * @return array
92469 */
92470 public function getAllClassNames();
92471 }
92472 <?php
92473
92474 /*
92475 * Copyright 2011 Johannes M. Schmitt <schmittjoh@gmail.com>
92476 *
92477 * Licensed under the Apache License, Version 2.0 (the "License");
92478 * you may not use this file except in compliance with the License.
92479 * You may obtain a copy of the License at
92480 *
92481 * http://www.apache.org/licenses/LICENSE-2.0
92482 *
92483 * Unless required by applicable law or agreed to in writing, software
92484 * distributed under the License is distributed on an "AS IS" BASIS,
92485 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
92486 * See the License for the specific language governing permissions and
92487 * limitations under the License.
92488 */
92489
92490 namespace Metadata;
92491
92492 /**
92493 * Base class for method metadata.
92494 *
92495 * This class is intended to be extended to add your application specific
92496 * properties, and flags.
92497 *
92498 * @author Johannes M. Schmitt <schmittjoh@gmail.com>
92499 */
92500 class MethodMetadata implements \Serializable
92501 {
92502 public $class;
92503 public $name;
92504 public $reflection;
92505
92506 public function __construct($class, $name)
92507 {
92508 $this->class = $class;
92509 $this->name = $name;
92510
92511 $this->reflection = new \ReflectionMethod($class, $name);
92512 $this->reflection->setAccessible(true);
92513 }
92514
92515 /**
92516 * @param object $obj
92517 * @param array $args
92518 *
92519 * @return mixed
92520 */
92521 public function invoke($obj, array $args = array())
92522 {
92523 return $this->reflection->invokeArgs($obj, $args);
92524 }
92525
92526 public function serialize()
92527 {
92528 return serialize(array($this->class, $this->name));
92529 }
92530
92531 public function unserialize($str)
92532 {
92533 list($this->class, $this->name) = unserialize($str);
92534
92535 $this->reflection = new \ReflectionMethod($this->class, $this->name);
92536 $this->reflection->setAccessible(true);
92537 }
92538 }
92539 <?php
92540
92541 /*
92542 * Copyright 2011 Johannes M. Schmitt <schmittjoh@gmail.com>
92543 *
92544 * Licensed under the Apache License, Version 2.0 (the "License");
92545 * you may not use this file except in compliance with the License.
92546 * You may obtain a copy of the License at
92547 *
92548 * http://www.apache.org/licenses/LICENSE-2.0
92549 *
92550 * Unless required by applicable law or agreed to in writing, software
92551 * distributed under the License is distributed on an "AS IS" BASIS,
92552 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
92553 * See the License for the specific language governing permissions and
92554 * limitations under the License.
92555 */
92556
92557 namespace Metadata;
92558
92559 /**
92560 * Interface for Metadata Factory implementations.
92561 *
92562 * @author Johannes M. Schmitt <schmittjoh@gmail.com>
92563 */
92564 interface MetadataFactoryInterface
92565 {
92566 /**
92567 * Returns the gathered metadata for the given class name.
92568 *
92569 * If the drivers return instances of MergeableClassMetadata, these will be
92570 * merged prior to returning. Otherwise, all metadata for the inheritance
92571 * hierarchy will be returned as ClassHierarchyMetadata unmerged.
92572 *
92573 * If no metadata is available, null is returned.
92574 *
92575 * @param string $className
92576 *
92577 * @return ClassHierarchyMetadata|MergeableClassMetadata|null
92578 */
92579 public function getMetadataForClass($className);
92580 }
92581 <?php
92582
92583 /*
92584 * Copyright 2011 Johannes M. Schmitt <schmittjoh@gmail.com>
92585 *
92586 * Licensed under the Apache License, Version 2.0 (the "License");
92587 * you may not use this file except in compliance with the License.
92588 * You may obtain a copy of the License at
92589 *
92590 * http://www.apache.org/licenses/LICENSE-2.0
92591 *
92592 * Unless required by applicable law or agreed to in writing, software
92593 * distributed under the License is distributed on an "AS IS" BASIS,
92594 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
92595 * See the License for the specific language governing permissions and
92596 * limitations under the License.
92597 */
92598
92599 namespace Metadata;
92600
92601 /**
92602 * Base class for class metadata.
92603 *
92604 * This class is intended to be extended to add your own application specific
92605 * properties, and flags.
92606 *
92607 * @author Johannes M. Schmitt <schmittjoh@gmail.com>
92608 */
92609 class ClassMetadata implements \Serializable
92610 {
92611 public $name;
92612 public $reflection;
92613 public $methodMetadata = array();
92614 public $propertyMetadata = array();
92615 public $fileResources = array();
92616 public $createdAt;
92617
92618 public function __construct($name)
92619 {
92620 $this->name = $name;
92621
92622 $this->reflection = new \ReflectionClass($name);
92623 $this->createdAt = time();
92624 }
92625
92626 public function addMethodMetadata(MethodMetadata $metadata)
92627 {
92628 $this->methodMetadata[$metadata->name] = $metadata;
92629 }
92630
92631 public function addPropertyMetadata(PropertyMetadata $metadata)
92632 {
92633 $this->propertyMetadata[$metadata->name] = $metadata;
92634 }
92635
92636 public function isFresh($timestamp = null)
92637 {
92638 if (null === $timestamp) {
92639 $timestamp = $this->createdAt;
92640 }
92641
92642 foreach ($this->fileResources as $filepath) {
92643 if (!file_exists($filepath)) {
92644 return false;
92645 }
92646
92647 if ($timestamp < filemtime($filepath)) {
92648 return false;
92649 }
92650 }
92651
92652 return true;
92653 }
92654
92655 public function serialize()
92656 {
92657 return serialize(array(
92658 $this->name,
92659 $this->methodMetadata,
92660 $this->propertyMetadata,
92661 $this->fileResources,
92662 $this->createdAt,
92663 ));
92664 }
92665
92666 public function unserialize($str)
92667 {
92668 list(
92669 $this->name,
92670 $this->methodMetadata,
92671 $this->propertyMetadata,
92672 $this->fileResources,
92673 $this->createdAt
92674 ) = unserialize($str);
92675
92676 $this->reflection = new \ReflectionClass($this->name);
92677 }
92678 }
92679 <?php
92680
92681 namespace Metadata;
92682
92683 class MergeableClassMetadata extends ClassMetadata implements MergeableInterface
92684 {
92685 public function merge(MergeableInterface $object)
92686 {
92687 if (!$object instanceof MergeableClassMetadata) {
92688 throw new \InvalidArgumentException('$object must be an instance of MergeableClassMetadata.');
92689 }
92690
92691 $this->name = $object->name;
92692 $this->reflection = $object->reflection;
92693 $this->methodMetadata = array_merge($this->methodMetadata, $object->methodMetadata);
92694 $this->propertyMetadata = array_merge($this->propertyMetadata, $object->propertyMetadata);
92695 $this->fileResources = array_merge($this->fileResources, $object->fileResources);
92696
92697 if ($object->createdAt < $this->createdAt) {
92698 $this->createdAt = $object->createdAt;
92699 }
92700 }
92701 }<?php
92702
92703 namespace Metadata;
92704
92705 interface MergeableInterface
92706 {
92707 /**
92708 * @param MergeableInterface $object
92709 *
92710 * @return void
92711 */
92712 public function merge(MergeableInterface $object);
92713 }
92714 <?php
92715
92716 /*
92717 * Copyright 2011 Johannes M. Schmitt <schmittjoh@gmail.com>
92718 *
92719 * Licensed under the Apache License, Version 2.0 (the "License");
92720 * you may not use this file except in compliance with the License.
92721 * You may obtain a copy of the License at
92722 *
92723 * http://www.apache.org/licenses/LICENSE-2.0
92724 *
92725 * Unless required by applicable law or agreed to in writing, software
92726 * distributed under the License is distributed on an "AS IS" BASIS,
92727 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
92728 * See the License for the specific language governing permissions and
92729 * limitations under the License.
92730 */
92731
92732 namespace Metadata;
92733
92734 /**
92735 * Represents the metadata for the entire class hierarchy.
92736 *
92737 * @author Johannes M. Schmitt <schmittjoh@gmail.com>
92738 */
92739 class ClassHierarchyMetadata
92740 {
92741 public $classMetadata = array();
92742
92743 public function addClassMetadata(ClassMetadata $metadata)
92744 {
92745 $this->classMetadata[$metadata->name] = $metadata;
92746 }
92747
92748 public function getRootClassMetadata()
92749 {
92750 return reset($this->classMetadata);
92751 }
92752
92753 public function getOutsideClassMetadata()
92754 {
92755 return end($this->classMetadata);
92756 }
92757
92758 public function isFresh($timestamp)
92759 {
92760 foreach ($this->classMetadata as $metadata) {
92761 if (!$metadata->isFresh($timestamp)) {
92762 return false;
92763 }
92764 }
92765
92766 return true;
92767 }
92768 }
92769 <?php
92770
92771 /*
92772 * This file is part of the Metadata library.
92773 *
92774 * (C) 2011 Johannes M. Schmitt <schmittjoh@gmail.com>
92775 *
92776 */
92777
92778 spl_autoload_register(function($class) {
92779 if (0 === strpos($class, 'Metadata\Tests\\')) {
92780 $path = __DIR__.'/../tests/'.strtr($class, '\\', '/').'.php';
92781 if (file_exists($path) && is_readable($path)) {
92782 require_once $path;
92783
92784 return true;
92785 }
92786 } elseif (0 === strpos($class, 'Metadata\\')) {
92787 $path = __DIR__.'/../src/'.($class = strtr($class, '\\', '/')).'.php';
92788 if (file_exists($path) && is_readable($path)) {
92789 require_once $path;
92790
92791 return true;
92792 }
92793 } elseif (0 === strpos($class, 'Symfony\\')) {
92794 $path = __DIR__.'/../../symfony/src/'.strtr($class, '\\', '/').'.php';
92795
92796 if (file_exists($path) && is_readable($path)) {
92797 require_once $path;
92798
92799 return true;
92800 }
92801 }
92802 });
92803
92804 @include __DIR__ . '/../vendor/.composer/autoload.php';
92805 <?php
92806
92807 namespace Metadata\Tests\Driver;
92808
92809 use Metadata\Driver\FileLocator;
92810
92811 class FileLocatorTest extends \PHPUnit_Framework_TestCase
92812 {
92813 public function testFindFileForClass()
92814 {
92815 $locator = new FileLocator(array(
92816 'Metadata\Tests\Driver\Fixture\A' => __DIR__.'/Fixture/A',
92817 'Metadata\Tests\Driver\Fixture\B' => __DIR__.'/Fixture/B',
92818 'Metadata\Tests\Driver\Fixture\C' => __DIR__.'/Fixture/C',
92819 ));
92820
92821 $ref = new \ReflectionClass('Metadata\Tests\Driver\Fixture\A\A');
92822 $this->assertEquals(realpath(__DIR__.'/Fixture/A/A.xml'), realpath($locator->findFileForClass($ref, 'xml')));
92823
92824 $ref = new \ReflectionClass('Metadata\Tests\Driver\Fixture\B\B');
92825 $this->assertNull($locator->findFileForClass($ref, 'xml'));
92826
92827 $ref = new \ReflectionClass('Metadata\Tests\Driver\Fixture\C\SubDir\C');
92828 $this->assertEquals(realpath(__DIR__.'/Fixture/C/SubDir.C.yml'), realpath($locator->findFileForClass($ref, 'yml')));
92829 }
92830
92831 public function testTraits()
92832 {
92833 if (version_compare(PHP_VERSION, '5.4.0', '<')) {
92834 $this->markTestSkipped('No traits available');
92835 }
92836
92837 $locator = new FileLocator(array(
92838 'Metadata\Tests\Driver\Fixture\T' => __DIR__.'/Fixture/T',
92839 ));
92840
92841 $ref = new \ReflectionClass('Metadata\Tests\Driver\Fixture\T\T');
92842 $this->assertEquals(realpath(__DIR__.'/Fixture/T/T.xml'), realpath($locator->findFileForClass($ref, 'xml')));
92843 }
92844
92845 public function testFindFileForGlobalNamespacedClass()
92846 {
92847 $locator = new FileLocator(array(
92848 '' => __DIR__.'/Fixture/D',
92849 ));
92850
92851 require_once __DIR__.'/Fixture/D/D.php';
92852 $ref = new \ReflectionClass('D');
92853 $this->assertEquals(realpath(__DIR__.'/Fixture/D/D.yml'), realpath($locator->findFileForClass($ref, 'yml')));
92854 }
92855
92856 public function testFindAllFiles()
92857 {
92858 $locator = new FileLocator(array(
92859 'Metadata\Tests\Driver\Fixture\A' => __DIR__.'/Fixture/A',
92860 'Metadata\Tests\Driver\Fixture\B' => __DIR__.'/Fixture/B',
92861 'Metadata\Tests\Driver\Fixture\C' => __DIR__.'/Fixture/C',
92862 'Metadata\Tests\Driver\Fixture\D' => __DIR__.'/Fixture/D'
92863 ));
92864
92865 $this->assertCount(1, $xmlFiles = $locator->findAllClasses('xml'));
92866 $this->assertSame('Metadata\Tests\Driver\Fixture\A\A', $xmlFiles[0]);
92867
92868 $this->assertCount(3, $ymlFiles = $locator->findAllClasses('yml'));
92869 $this->assertSame('Metadata\Tests\Driver\Fixture\B\B', $ymlFiles[0]);
92870 $this->assertSame('Metadata\Tests\Driver\Fixture\C\SubDir\C', $ymlFiles[1]);
92871 $this->assertSame('Metadata\Tests\Driver\Fixture\D\D', $ymlFiles[2]);
92872 }
92873 }
92874 <?php
92875
92876 namespace Metadata\Tests\Driver;
92877
92878 use Metadata\ClassMetadata;
92879
92880 /**
92881 * @author Jordan Stout <j@jrdn.org>
92882 */
92883 class AbstractFileDriverTest extends \PHPUnit_Framework_TestCase
92884 {
92885 private static $extension = 'jms_metadata.yml';
92886
92887 /** @var \PHPUnit_Framework_MockObject_MockObject */
92888 private $locator;
92889
92890 /** @var \PHPUnit_Framework_MockObject_MockObject */
92891 private $driver;
92892
92893 public function setUp()
92894 {
92895 $this->locator = $this->getMock('Metadata\Driver\FileLocator', array(), array(), '', false);
92896 $this->driver = $this->getMockBuilder('Metadata\Driver\AbstractFileDriver')
92897 ->setConstructorArgs(array($this->locator))
92898 ->getMockForAbstractClass();
92899
92900 $this->driver->expects($this->any())->method('getExtension')->will($this->returnValue(self::$extension));
92901 }
92902
92903 public function testLoadMetadataForClass()
92904 {
92905 $class = new \ReflectionClass('\stdClass');
92906 $this->locator
92907 ->expects($this->once())
92908 ->method('findFileForClass')
92909 ->with($class, self::$extension)
92910 ->will($this->returnValue('Some\Path'));
92911
92912 $this->driver
92913 ->expects($this->once())
92914 ->method('loadMetadataFromFile')
92915 ->with($class, 'Some\Path')
92916 ->will($this->returnValue($metadata = new ClassMetadata('\stdClass')));
92917
92918 $this->assertSame($metadata, $this->driver->loadMetadataForClass($class));
92919 }
92920
92921 public function testLoadMetadataForClassWillReturnNull()
92922 {
92923 $class = new \ReflectionClass('\stdClass');
92924 $this->locator
92925 ->expects($this->once())
92926 ->method('findFileForClass')
92927 ->with($class, self::$extension)
92928 ->will($this->returnValue(null));
92929
92930 $this->assertSame(null, $this->driver->loadMetadataForClass($class));
92931 }
92932
92933 public function testGetAllClassNames()
92934 {
92935 $class = new \ReflectionClass('\stdClass');
92936 $this->locator
92937 ->expects($this->once())
92938 ->method('findAllClasses')
92939 ->with(self::$extension)
92940 ->will($this->returnValue(array('\stdClass')));
92941
92942 $this->assertSame(array('\stdClass'), $this->driver->getAllClassNames($class));
92943 }
92944
92945 public function testGetAllClassNamesThrowsRuntimeException()
92946 {
92947 $this->setExpectedException('RuntimeException');
92948
92949 $locator = $this->getMock('Metadata\Driver\FileLocatorInterface', array(), array(), '', false);
92950 $driver = $this->getMockBuilder('Metadata\Driver\AbstractFileDriver')
92951 ->setConstructorArgs(array($locator))
92952 ->getMockForAbstractClass();
92953 $class = new \ReflectionClass('\stdClass');
92954 $locator->expects($this->never())->method('findAllClasses');
92955
92956 $driver->getAllClassNames($class);
92957 }
92958 }
92959 <?php
92960
92961 namespace Metadata\Tests\Driver;
92962
92963 use Metadata\ClassMetadata;
92964 use Metadata\Driver\DriverChain;
92965
92966 class DriverChainTest extends \PHPUnit_Framework_TestCase
92967 {
92968 public function testLoadMetadataForClass()
92969 {
92970 $driver = $this->getMock('Metadata\\Driver\\DriverInterface');
92971 $driver
92972 ->expects($this->once())
92973 ->method('loadMetadataForClass')
92974 ->will($this->returnValue($metadata = new ClassMetadata('\stdClass')))
92975 ;
92976 $chain = new DriverChain(array($driver));
92977
92978 $this->assertSame($metadata, $chain->loadMetadataForClass(new \ReflectionClass('\stdClass')));
92979 }
92980
92981 public function testGetAllClassNames()
92982 {
92983 $driver1 = $this->getMock('Metadata\\Driver\\AdvancedDriverInterface');
92984 $driver1
92985 ->expects($this->once())
92986 ->method('getAllClassNames')
92987 ->will($this->returnValue(array('Foo')));
92988
92989 $driver2 = $this->getMock('Metadata\\Driver\\AdvancedDriverInterface');
92990 $driver2
92991 ->expects($this->once())
92992 ->method('getAllClassNames')
92993 ->will($this->returnValue(array('Bar')));
92994
92995 $chain = new DriverChain(array($driver1, $driver2));
92996
92997 $this->assertSame(array('Foo', 'Bar'), $chain->getAllClassNames());
92998 }
92999
93000 public function testLoadMetadataForClassReturnsNullWhenNoMetadataIsFound()
93001 {
93002 $driver = new DriverChain(array());
93003 $this->assertNull($driver->loadMetadataForClass(new \ReflectionClass('\stdClass')));
93004
93005 $driver = $this->getMock('Metadata\\Driver\\DriverInterface');
93006 $driver
93007 ->expects($this->once())
93008 ->method('loadMetadataForClass')
93009 ->will($this->returnValue(null))
93010 ;
93011 $driverChain = new DriverChain(array($driver));
93012 $this->assertNull($driver->loadMetadataForClass(new \ReflectionClass('\stdClass')));
93013 }
93014
93015 public function testGetAllClassNamesThrowsException()
93016 {
93017 $this->setExpectedException('RuntimeException');
93018 $driver = $this->getMock('Metadata\\Driver\\DriverInterface');
93019 $driver->expects($this->never())->method('getAllClassNames');
93020 $chain = new DriverChain(array($driver));
93021 $chain->getAllClassNames();
93022 }
93023 }
93024 <?php
93025
93026 class D { }
93027 <?php
93028
93029 namespace Metadata\Tests\Driver\Fixture\B;
93030
93031 class B { }<?php
93032
93033 namespace Metadata\Tests\Driver\Fixture\T;
93034
93035 trait T {}
93036 <?php
93037
93038 namespace Metadata\Tests\Driver\Fixture\C\SubDir;
93039
93040 class C { }<?php
93041
93042 namespace Metadata\Tests\Driver\Fixture\A;
93043
93044 class A {}
93045 <?php
93046
93047 namespace Metadata\Tests\Cache;
93048
93049 use Metadata\ClassMetadata;
93050
93051 use Metadata\Cache\FileCache;
93052
93053 class FileCacheTest extends \PHPUnit_Framework_TestCase
93054 {
93055 public function testLoadEvictPutClassMetadataFromInCache()
93056 {
93057 $cache = new FileCache(sys_get_temp_dir());
93058
93059 $this->assertNull($cache->loadClassMetadataFromCache($refl = new \ReflectionClass('Metadata\Tests\Fixtures\TestObject')));
93060 $cache->putClassMetadataInCache($metadata = new ClassMetadata('Metadata\Tests\Fixtures\TestObject'));
93061
93062 $this->assertEquals($metadata, $cache->loadClassMetadataFromCache($refl));
93063
93064 $cache->evictClassMetadataFromCache($refl);
93065 $this->assertNull($cache->loadClassMetadataFromCache($refl));
93066 }
93067 }<?php
93068
93069 namespace Metadata\Tests\Cache;
93070
93071 use Metadata\ClassMetadata;
93072 use Metadata\Cache\DoctrineCacheAdapter;
93073 use Doctrine\Common\Cache\ArrayCache;
93074
93075 class DoctrineCacheAdapterTest extends \PHPUnit_Framework_TestCase
93076 {
93077 public function setUp()
93078 {
93079 if (!interface_exists('Doctrine\Common\Cache\Cache')) {
93080 $this->markTestSkipped('Doctrine\Common is not installed.');
93081 }
93082 }
93083
93084 public function testLoadEvictPutClassMetadataFromInCache()
93085 {
93086 $cache = new DoctrineCacheAdapter('metadata-test', new ArrayCache());
93087
93088 $this->assertNull($cache->loadClassMetadataFromCache($refl = new \ReflectionClass('Metadata\Tests\Fixtures\TestObject')));
93089 $cache->putClassMetadataInCache($metadata = new ClassMetadata('Metadata\Tests\Fixtures\TestObject'));
93090
93091 $this->assertEquals($metadata, $cache->loadClassMetadataFromCache($refl));
93092
93093 $cache->evictClassMetadataFromCache($refl);
93094 $this->assertNull($cache->loadClassMetadataFromCache($refl));
93095 }
93096 }
93097 <?php
93098
93099 namespace Metadata\Tests;
93100
93101 use Metadata\MergeableClassMetadata;
93102 use Metadata\ClassMetadata;
93103
93104 class MergeableClassMetadataTest extends \PHPUnit_Framework_TestCase
93105 {
93106 public function testMerge()
93107 {
93108 $parentMetadata = new MergeableClassMetadata('Metadata\Tests\Fixtures\TestParent');
93109 $parentMetadata->propertyMetadata['foo'] = 'bar';
93110 $parentMetadata->propertyMetadata['baz'] = 'baz';
93111 $parentMetadata->methodMetadata['foo'] = 'bar';
93112 $parentMetadata->createdAt = 2;
93113 $parentMetadata->fileResources[] = 'foo';
93114
93115 $childMetadata = new MergeableClassMetadata('Metadata\Tests\Fixtures\TestObject');
93116 $childMetadata->propertyMetadata['foo'] = 'baz';
93117 $childMetadata->methodMetadata['foo'] = 'baz';
93118 $childMetadata->createdAt = 1;
93119 $childMetadata->fileResources[] = 'bar';
93120
93121 $parentMetadata->merge($childMetadata);
93122 $this->assertEquals('Metadata\Tests\Fixtures\TestObject', $parentMetadata->name);
93123 $this->assertEquals('Metadata\Tests\Fixtures\TestObject', $parentMetadata->reflection->name);
93124 $this->assertEquals(array('foo' => 'baz', 'baz' => 'baz',), $parentMetadata->propertyMetadata);
93125 $this->assertEquals(array('foo' => 'baz',), $parentMetadata->methodMetadata);
93126 $this->assertEquals(1, $parentMetadata->createdAt);
93127 $this->assertEquals(array('foo', 'bar'), $parentMetadata->fileResources);
93128 }
93129 }<?php
93130
93131 namespace Metadata\Tests;
93132
93133 use Metadata\PropertyMetadata;
93134 use Metadata\MergeableClassMetadata;
93135 use Metadata\ClassMetadata;
93136 use Metadata\MetadataFactory;
93137
93138 class MetadataFactoryTest extends \PHPUnit_Framework_TestCase
93139 {
93140 public function testGetMetadataForClass()
93141 {
93142 $driver = $this->getMock('Metadata\Driver\DriverInterface');
93143
93144 $driver
93145 ->expects($this->at(0))
93146 ->method('loadMetadataForClass')
93147 ->with($this->equalTo(new \ReflectionClass('Metadata\Tests\Fixtures\TestObject')))
93148 ->will($this->returnCallback(function($class) {
93149 return new ClassMetadata($class->getName());
93150 }))
93151 ;
93152 $driver
93153 ->expects($this->at(1))
93154 ->method('loadMetadataForClass')
93155 ->with($this->equalTo(new \ReflectionClass('Metadata\Tests\Fixtures\TestParent')))
93156 ->will($this->returnCallback(function($class) {
93157 return new ClassMetadata($class->getName());
93158 }))
93159 ;
93160
93161 $factory = new MetadataFactory($driver);
93162 $metadata = $factory->getMetadataForClass('Metadata\Tests\Fixtures\TestParent');
93163
93164 $this->assertInstanceOf('Metadata\ClassHierarchyMetadata', $metadata);
93165 $this->assertEquals(2, count($metadata->classMetadata));
93166 }
93167
93168 public function testGetMetadataForClassWhenMergeable()
93169 {
93170 $driver = $this->getMock('Metadata\Driver\DriverInterface');
93171
93172 $driver
93173 ->expects($this->at(0))
93174 ->method('loadMetadataForClass')
93175 ->with($this->equalTo(new \ReflectionClass('Metadata\Tests\Fixtures\TestObject')))
93176 ->will($this->returnCallback(function($class) {
93177 return new MergeableClassMetadata($class->getName());
93178 }))
93179 ;
93180 $driver
93181 ->expects($this->at(1))
93182 ->method('loadMetadataForClass')
93183 ->with($this->equalTo(new \ReflectionClass('Metadata\Tests\Fixtures\TestParent')))
93184 ->will($this->returnCallback(function($class) {
93185 return new MergeableClassMetadata($class->getName());
93186 }))
93187 ;
93188
93189 $factory = new MetadataFactory($driver);
93190 $metadata = $factory->getMetadataForClass('Metadata\Tests\Fixtures\TestParent');
93191
93192 $this->assertInstanceOf('Metadata\MergeableClassMetadata', $metadata);
93193 $this->assertEquals('Metadata\Tests\Fixtures\TestParent', $metadata->name);
93194 }
93195
93196 public function testGetMetadataWithComplexHierarchy()
93197 {
93198 $driver = $this->getMock('Metadata\Driver\DriverInterface');
93199
93200 $driver
93201 ->expects($this->any())
93202 ->method('loadMetadataForClass')
93203 ->will($this->returnCallback(function($class) {
93204 $metadata = new MergeableClassMetadata($class->name);
93205
93206 switch ($class->name) {
93207 case 'Metadata\Tests\Fixtures\ComplexHierarchy\BaseClass':
93208 $metadata->propertyMetadata['foo'] = new PropertyMetadata($class->name, 'foo');
93209 break;
93210
93211 case 'Metadata\Tests\Fixtures\ComplexHierarchy\SubClassA':
93212 $metadata->propertyMetadata['bar'] = new PropertyMetadata($class->name, 'bar');
93213 break;
93214
93215 case 'Metadata\Tests\Fixtures\ComplexHierarchy\SubClassB':
93216 $metadata->propertyMetadata['baz'] = new PropertyMetadata($class->name, 'baz');
93217 break;
93218
93219 default:
93220 throw new \RuntimeException(sprintf('Unsupported class "%s".', $class->name));
93221 }
93222
93223 return $metadata;
93224 }))
93225 ;
93226
93227 $factory = new MetadataFactory($driver);
93228
93229 $subClassA = $factory->getMetadataForClass('Metadata\Tests\Fixtures\ComplexHierarchy\SubClassA');
93230 $this->assertInstanceOf('Metadata\MergeableClassMetadata', $subClassA);
93231 $this->assertEquals(array('foo', 'bar'), array_keys($subClassA->propertyMetadata));
93232
93233 $subClassB = $factory->getMetadataForClass('Metadata\Tests\Fixtures\ComplexHierarchy\SubClassB');
93234 $this->assertInstanceOf('Metadata\MergeableClassMetadata', $subClassB);
93235 $this->assertEquals(array('foo', 'baz'), array_keys($subClassB->propertyMetadata));
93236 }
93237
93238 public function testGetMetadataWithCache()
93239 {
93240 $driver = $this->getMock('Metadata\Driver\DriverInterface');
93241 $driver
93242 ->expects($this->once())
93243 ->method('loadMetadataForClass')
93244 ->will($this->returnValue($metadata = new ClassMetadata('Metadata\Tests\Fixtures\TestObject')))
93245 ;
93246
93247 $factory = new MetadataFactory($driver);
93248
93249 $cache = $this->getMock('Metadata\Cache\CacheInterface');
93250 $cache
93251 ->expects($this->once())
93252 ->method('loadClassMetadataFromCache')
93253 ->with($this->equalTo(new \ReflectionClass('Metadata\Tests\Fixtures\TestObject')))
93254 ->will($this->returnValue(null))
93255 ;
93256 $cache
93257 ->expects($this->once())
93258 ->method('putClassMetadataInCache')
93259 ->with($this->equalTo($metadata))
93260 ;
93261 $factory->setCache($cache);
93262
93263 $this->assertSame($metadata, reset($factory->getMetadataForClass('Metadata\Tests\Fixtures\TestObject')->classMetadata));
93264 }
93265
93266 public function testGetMetadataReturnsNullIfNoMetadataIsFound()
93267 {
93268 $driver = $this->getMock('Metadata\Driver\DriverInterface');
93269 $driver
93270 ->expects($this->once())
93271 ->method('loadMetadataForClass')
93272 ->will($this->returnValue(null))
93273 ;
93274
93275 $factory = new MetadataFactory($driver);
93276
93277 $this->assertNull($factory->getMetadataForClass('stdClass'));
93278 }
93279
93280 public function testGetMetadataWithInterfaces()
93281 {
93282 $driver = $this->getMock('Metadata\Driver\DriverInterface');
93283
93284 $driver
93285 ->expects($this->at(3))
93286 ->method('loadMetadataForClass')
93287 ->with($this->equalTo(new \ReflectionClass('Metadata\Tests\Fixtures\ComplexHierarchy\SubClassA')))
93288 ;
93289 $driver
93290 ->expects($this->at(2))
93291 ->method('loadMetadataForClass')
93292 ->with($this->equalTo(new \ReflectionClass('Metadata\Tests\Fixtures\ComplexHierarchy\InterfaceB')))
93293 ;
93294 $driver
93295 ->expects($this->at(1))
93296 ->method('loadMetadataForClass')
93297 ->with($this->equalTo(new \ReflectionClass('Metadata\Tests\Fixtures\ComplexHierarchy\BaseClass')))
93298 ;
93299 $driver
93300 ->expects($this->at(0))
93301 ->method('loadMetadataForClass')
93302 ->with($this->equalTo(new \ReflectionClass('Metadata\Tests\Fixtures\ComplexHierarchy\InterfaceA')))
93303 ;
93304
93305 $factory = new MetadataFactory($driver);
93306 $factory->setIncludeInterfaces(true);
93307
93308 $factory->getMetadataForClass('Metadata\Tests\Fixtures\ComplexHierarchy\SubClassA');
93309 }
93310
93311 public function testGetAllClassNames()
93312 {
93313 $driver = $this->getMock('Metadata\Driver\AdvancedDriverInterface');
93314 $driver
93315 ->expects($this->once())
93316 ->method('getAllClassNames')
93317 ->will($this->returnValue(array()));
93318
93319 $factory = new MetadataFactory($driver);
93320 $this->assertSame(array(), $factory->getAllClassNames());
93321 }
93322
93323 public function testGetAllClassNamesThrowsException()
93324 {
93325 $this->setExpectedException('RuntimeException');
93326 $factory = new MetadataFactory($this->getMock('Metadata\Driver\DriverInterface'));
93327 $factory->getAllClassNames();
93328 }
93329 }
93330 <?php
93331
93332 namespace Metadata\Tests;
93333
93334 use Metadata\Tests\Fixtures\TestObject;
93335
93336 use Metadata\PropertyMetadata;
93337
93338 class PropertyMetadataTest extends \PHPUnit_Framework_TestCase
93339 {
93340 public function testConstructor()
93341 {
93342 $metadata = new PropertyMetadata('Metadata\Tests\Fixtures\TestObject', 'foo');
93343
93344 $this->assertEquals('Metadata\Tests\Fixtures\TestObject', $metadata->class);
93345 $this->assertEquals('foo', $metadata->name);
93346 $this->assertEquals(new \ReflectionProperty('Metadata\Tests\Fixtures\TestObject', 'foo'), $metadata->reflection);
93347 }
93348
93349 public function testSerializeUnserialize()
93350 {
93351 $metadata = new PropertyMetadata('Metadata\Tests\Fixtures\TestObject', 'foo');
93352
93353 $this->assertEquals($metadata, unserialize(serialize($metadata)));
93354 }
93355
93356 public function testSetGetValue()
93357 {
93358 $obj = new TestObject();
93359 $metadata = new PropertyMetadata('Metadata\Tests\Fixtures\TestObject', 'foo');
93360
93361 $this->assertNull($metadata->getValue($obj));
93362 $metadata->setValue($obj, 'foo');
93363 $this->assertEquals('foo', $metadata->getValue($obj));
93364 }
93365 }<?php
93366
93367 namespace Metadata\Tests\Fixtures;
93368
93369 class TestParent extends TestObject
93370 {
93371 }<?php
93372
93373 namespace Metadata\Tests\Fixtures;
93374
93375 class TestObject
93376 {
93377 private $foo;
93378
93379 public function getFoo()
93380 {
93381 return $this->foo;
93382 }
93383
93384 private function setFoo($foo)
93385 {
93386 $this->foo = $foo;
93387 }
93388 }
93389 <?php
93390
93391 namespace Metadata\Tests\Fixtures\ComplexHierarchy;
93392
93393 interface InterfaceB
93394 {
93395 }
93396 <?php
93397
93398 namespace Metadata\Tests\Fixtures\ComplexHierarchy;
93399
93400 interface InterfaceA
93401 {
93402 }
93403 <?php
93404
93405 namespace Metadata\Tests\Fixtures\ComplexHierarchy;
93406
93407 abstract class BaseClass implements InterfaceA
93408 {
93409 private $foo;
93410 }<?php
93411
93412 namespace Metadata\Tests\Fixtures\ComplexHierarchy;
93413
93414 class SubClassA extends BaseClass implements InterfaceA, InterfaceB
93415 {
93416 private $bar;
93417 }<?php
93418
93419 namespace Metadata\Tests\Fixtures\ComplexHierarchy;
93420
93421 class SubClassB extends BaseClass
93422 {
93423 private $baz;
93424 }<?php
93425
93426 namespace Metadata\Tests;
93427
93428 use Metadata\ClassMetadata;
93429
93430 class ClassMetadataTest extends \PHPUnit_Framework_TestCase
93431 {
93432 public function testConstructor()
93433 {
93434 $metadata = new ClassMetadata('Metadata\Tests\Fixtures\TestObject');
93435
93436 $this->assertEquals('Metadata\Tests\Fixtures\TestObject', $metadata->name);
93437 $this->assertEquals('Metadata\Tests\Fixtures\TestObject', $metadata->reflection->name);
93438 }
93439
93440 public function testSerializeUnserialize()
93441 {
93442 $metadata = new ClassMetadata('Metadata\Tests\Fixtures\TestObject');
93443
93444 $this->assertEquals($metadata, unserialize(serialize($metadata)));
93445 }
93446
93447 public function testIsFresh()
93448 {
93449 $ref = new \ReflectionClass('Metadata\Tests\Fixtures\TestObject');
93450 touch($ref->getFilename());
93451 sleep(2);
93452
93453 $metadata = new ClassMetadata($ref->name);
93454 $metadata->fileResources[] = $ref->getFilename();
93455 $this->assertTrue($metadata->isFresh());
93456
93457 sleep(2);
93458 clearstatcache($ref->getFilename());
93459 touch($ref->getFilename());
93460 $this->assertFalse($metadata->isFresh());
93461 }
93462 }
93463 <?php
93464
93465 namespace Metadata\Tests;
93466
93467 use Metadata\Tests\Fixtures\TestObject;
93468
93469 use Metadata\MethodMetadata;
93470
93471 class MethodMetadataTest extends \PHPUnit_Framework_TestCase
93472 {
93473 public function testConstructor()
93474 {
93475 $metadata = new MethodMetadata('Metadata\Tests\Fixtures\TestObject', 'setFoo');
93476
93477 $this->assertEquals('Metadata\Tests\Fixtures\TestObject', $metadata->class);
93478 $this->assertEquals('setFoo', $metadata->name);
93479 $this->assertEquals(new \ReflectionMethod('Metadata\Tests\Fixtures\TestObject', 'setFoo'), $metadata->reflection);
93480 }
93481
93482 public function testSerializeUnserialize()
93483 {
93484 $metadata = new MethodMetadata('Metadata\Tests\Fixtures\TestObject', 'setFoo');
93485
93486 $this->assertEquals($metadata, unserialize(serialize($metadata)));
93487 }
93488
93489 public function testInvoke()
93490 {
93491 $obj = new TestObject();
93492 $metadata = new MethodMetadata('Metadata\Tests\Fixtures\TestObject', 'setFoo');
93493
93494 $this->assertNull($obj->getFoo());
93495 $metadata->invoke($obj, array('foo'));
93496 $this->assertEquals('foo', $obj->getFoo());
93497 }
93498 }CHANGELOG
93499 =========
93500
93501 This changelog references all relevant changes:
93502
93503 To get the diff between the two last versions, go to
93504 https://github.com/schmittjoh/metadata/compare/1.0.0...1.1.0
93505
93506 * 1.4.1 (2013-08-26)
93507 * fixes a possible permission issue when using filesystem ACLs
93508
93509 * 1.4.0 (2013-08-25)
93510 * fixes a race condition when writing cache files
93511
93512 * 1.3.0 (2013-01-22)
93513 * added ability to retrieve all managed classes from the metadata factory
93514
93515 * 1.2.0 (2012-08-21)
93516 * added a Doctrine Cache Adapter
93517 * better support for traits, and classes in the global namespace
93518
93519 * 1.1.0 (2011-10-04)
93520
93521 * added support for metadata on interfaces
93522 * added support for non annotation-based drivers
93523 * added support for merging metadata
93524
93525 This release is fully backwards compatible with the 1.0.0 release. Therefore,
93526 the 1.0.x branch has been discontinued.
93527
93528 * 1.0.0 (2011-07-09)
93529 {
93530 "name": "jms/metadata",
93531 "description": "Class/method/property metadata management in PHP",
93532 "keywords": ["annotations","metadata","yaml","xml"],
93533 "type": "library",
93534 "license": "Apache",
93535 "authors": [
93536 {
93537 "name": "Johannes M. Schmitt",
93538 "email": "schmittjoh@gmail.com"
93539 }
93540 ],
93541 "require": {
93542 "php": ">=5.3.0"
93543 },
93544 "require-dev" : {
93545 "doctrine/cache" : "~1.0"
93546 },
93547 "autoload": {
93548 "psr-0": { "Metadata\\": "src/" }
93549 },
93550 "extra": {
93551 "branch-alias": {
93552 "dev-master": "1.4.x-dev"
93553 }
93554 }
93555 }
93556 <?xml version="1.0" encoding="UTF-8"?>
93557
93558 <phpunit backupGlobals="false"
93559 backupStaticAttributes="false"
93560 colors="true"
93561 convertErrorsToExceptions="true"
93562 convertNoticesToExceptions="true"
93563 convertWarningsToExceptions="true"
93564 processIsolation="false"
93565 stopOnFailure="false"
93566 syntaxCheck="false"
93567 bootstrap="tests/bootstrap.php"
93568 >
93569 <testsuites>
93570 <testsuite name="Metadata Test Suite">
93571 <directory>./tests/Metadata/</directory>
93572 </testsuite>
93573 </testsuites>
93574
93575 <groups>
93576 <exclude>
93577 <group>performance</group>
93578 </exclude>
93579 </groups>
93580 </phpunit>
93581 Metadata is a library for class/method/property metadata management in PHP
93582 ==========================================================================
93583
93584 Overview
93585 --------
93586
93587 This library provides some commonly needed base classes for managing metadata
93588 for classes, methods and properties. The metadata can come from many different
93589 sources (annotations, YAML/XML/PHP configuration files).
93590
93591 The metadata classes are used to abstract away that source and provide a common
93592 interface for all of them.
93593
93594 Usage
93595 -----
93596
93597 The library provides three classes that you can extend to add your application
93598 specific properties, and flags: ``ClassMetadata``, ``MethodMetadata``, and
93599 ``PropertyMetadata``
93600
93601 After you have added, your properties in sub-classes, you also need to add
93602 ``DriverInterface`` implementations which know how to populate these classes
93603 from the different metadata sources.
93604
93605 Finally, you can use the ``MetadataFactory`` to retrieve the metadata::
93606
93607 <?php
93608
93609 use Metadata\MetadataFactory;
93610 use Metadata\Driver\DriverChain;
93611
93612 $driver = new DriverChain(array(
93613 /** Annotation, YAML, XML, PHP, ... drivers */
93614 ));
93615 $factory = new MetadataFactory($driver);
93616 $metadata = $factory->getMetadataForClass('MyNamespace\MyObject');
93617
93618 Parser Library
93619 ==============
93620
93621 Learn more about it in its [documentation](http://jmsyst.com/libs/parser-lib).
93622 Apache License
93623 Version 2.0, January 2004
93624 http://www.apache.org/licenses/
93625
93626 TERMS AND CONDITIONS FOR USE, REPRODUCTION, AND DISTRIBUTION
93627
93628 1. Definitions.
93629
93630 "License" shall mean the terms and conditions for use, reproduction,
93631 and distribution as defined by Sections 1 through 9 of this document.
93632
93633 "Licensor" shall mean the copyright owner or entity authorized by
93634 the copyright owner that is granting the License.
93635
93636 "Legal Entity" shall mean the union of the acting entity and all
93637 other entities that control, are controlled by, or are under common
93638 control with that entity. For the purposes of this definition,
93639 "control" means (i) the power, direct or indirect, to cause the
93640 direction or management of such entity, whether by contract or
93641 otherwise, or (ii) ownership of fifty percent (50%) or more of the
93642 outstanding shares, or (iii) beneficial ownership of such entity.
93643
93644 "You" (or "Your") shall mean an individual or Legal Entity
93645 exercising permissions granted by this License.
93646
93647 "Source" form shall mean the preferred form for making modifications,
93648 including but not limited to software source code, documentation
93649 source, and configuration files.
93650
93651 "Object" form shall mean any form resulting from mechanical
93652 transformation or translation of a Source form, including but
93653 not limited to compiled object code, generated documentation,
93654 and conversions to other media types.
93655
93656 "Work" shall mean the work of authorship, whether in Source or
93657 Object form, made available under the License, as indicated by a
93658 copyright notice that is included in or attached to the work
93659 (an example is provided in the Appendix below).
93660
93661 "Derivative Works" shall mean any work, whether in Source or Object
93662 form, that is based on (or derived from) the Work and for which the
93663 editorial revisions, annotations, elaborations, or other modifications
93664 represent, as a whole, an original work of authorship. For the purposes
93665 of this License, Derivative Works shall not include works that remain
93666 separable from, or merely link (or bind by name) to the interfaces of,
93667 the Work and Derivative Works thereof.
93668
93669 "Contribution" shall mean any work of authorship, including
93670 the original version of the Work and any modifications or additions
93671 to that Work or Derivative Works thereof, that is intentionally
93672 submitted to Licensor for inclusion in the Work by the copyright owner
93673 or by an individual or Legal Entity authorized to submit on behalf of
93674 the copyright owner. For the purposes of this definition, "submitted"
93675 means any form of electronic, verbal, or written communication sent
93676 to the Licensor or its representatives, including but not limited to
93677 communication on electronic mailing lists, source code control systems,
93678 and issue tracking systems that are managed by, or on behalf of, the
93679 Licensor for the purpose of discussing and improving the Work, but
93680 excluding communication that is conspicuously marked or otherwise
93681 designated in writing by the copyright owner as "Not a Contribution."
93682
93683 "Contributor" shall mean Licensor and any individual or Legal Entity
93684 on behalf of whom a Contribution has been received by Licensor and
93685 subsequently incorporated within the Work.
93686
93687 2. Grant of Copyright License. Subject to the terms and conditions of
93688 this License, each Contributor hereby grants to You a perpetual,
93689 worldwide, non-exclusive, no-charge, royalty-free, irrevocable
93690 copyright license to reproduce, prepare Derivative Works of,
93691 publicly display, publicly perform, sublicense, and distribute the
93692 Work and such Derivative Works in Source or Object form.
93693
93694 3. Grant of Patent License. Subject to the terms and conditions of
93695 this License, each Contributor hereby grants to You a perpetual,
93696 worldwide, non-exclusive, no-charge, royalty-free, irrevocable
93697 (except as stated in this section) patent license to make, have made,
93698 use, offer to sell, sell, import, and otherwise transfer the Work,
93699 where such license applies only to those patent claims licensable
93700 by such Contributor that are necessarily infringed by their
93701 Contribution(s) alone or by combination of their Contribution(s)
93702 with the Work to which such Contribution(s) was submitted. If You
93703 institute patent litigation against any entity (including a
93704 cross-claim or counterclaim in a lawsuit) alleging that the Work
93705 or a Contribution incorporated within the Work constitutes direct
93706 or contributory patent infringement, then any patent licenses
93707 granted to You under this License for that Work shall terminate
93708 as of the date such litigation is filed.
93709
93710 4. Redistribution. You may reproduce and distribute copies of the
93711 Work or Derivative Works thereof in any medium, with or without
93712 modifications, and in Source or Object form, provided that You
93713 meet the following conditions:
93714
93715 (a) You must give any other recipients of the Work or
93716 Derivative Works a copy of this License; and
93717
93718 (b) You must cause any modified files to carry prominent notices
93719 stating that You changed the files; and
93720
93721 (c) You must retain, in the Source form of any Derivative Works
93722 that You distribute, all copyright, patent, trademark, and
93723 attribution notices from the Source form of the Work,
93724 excluding those notices that do not pertain to any part of
93725 the Derivative Works; and
93726
93727 (d) If the Work includes a "NOTICE" text file as part of its
93728 distribution, then any Derivative Works that You distribute must
93729 include a readable copy of the attribution notices contained
93730 within such NOTICE file, excluding those notices that do not
93731 pertain to any part of the Derivative Works, in at least one
93732 of the following places: within a NOTICE text file distributed
93733 as part of the Derivative Works; within the Source form or
93734 documentation, if provided along with the Derivative Works; or,
93735 within a display generated by the Derivative Works, if and
93736 wherever such third-party notices normally appear. The contents
93737 of the NOTICE file are for informational purposes only and
93738 do not modify the License. You may add Your own attribution
93739 notices within Derivative Works that You distribute, alongside
93740 or as an addendum to the NOTICE text from the Work, provided
93741 that such additional attribution notices cannot be construed
93742 as modifying the License.
93743
93744 You may add Your own copyright statement to Your modifications and
93745 may provide additional or different license terms and conditions
93746 for use, reproduction, or distribution of Your modifications, or
93747 for any such Derivative Works as a whole, provided Your use,
93748 reproduction, and distribution of the Work otherwise complies with
93749 the conditions stated in this License.
93750
93751 5. Submission of Contributions. Unless You explicitly state otherwise,
93752 any Contribution intentionally submitted for inclusion in the Work
93753 by You to the Licensor shall be under the terms and conditions of
93754 this License, without any additional terms or conditions.
93755 Notwithstanding the above, nothing herein shall supersede or modify
93756 the terms of any separate license agreement you may have executed
93757 with Licensor regarding such Contributions.
93758
93759 6. Trademarks. This License does not grant permission to use the trade
93760 names, trademarks, service marks, or product names of the Licensor,
93761 except as required for reasonable and customary use in describing the
93762 origin of the Work and reproducing the content of the NOTICE file.
93763
93764 7. Disclaimer of Warranty. Unless required by applicable law or
93765 agreed to in writing, Licensor provides the Work (and each
93766 Contributor provides its Contributions) on an "AS IS" BASIS,
93767 WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or
93768 implied, including, without limitation, any warranties or conditions
93769 of TITLE, NON-INFRINGEMENT, MERCHANTABILITY, or FITNESS FOR A
93770 PARTICULAR PURPOSE. You are solely responsible for determining the
93771 appropriateness of using or redistributing the Work and assume any
93772 risks associated with Your exercise of permissions under this License.
93773
93774 8. Limitation of Liability. In no event and under no legal theory,
93775 whether in tort (including negligence), contract, or otherwise,
93776 unless required by applicable law (such as deliberate and grossly
93777 negligent acts) or agreed to in writing, shall any Contributor be
93778 liable to You for damages, including any direct, indirect, special,
93779 incidental, or consequential damages of any character arising as a
93780 result of this License or out of the use or inability to use the
93781 Work (including but not limited to damages for loss of goodwill,
93782 work stoppage, computer failure or malfunction, or any and all
93783 other commercial damages or losses), even if such Contributor
93784 has been advised of the possibility of such damages.
93785
93786 9. Accepting Warranty or Additional Liability. While redistributing
93787 the Work or Derivative Works thereof, You may choose to offer,
93788 and charge a fee for, acceptance of support, warranty, indemnity,
93789 or other liability obligations and/or rights consistent with this
93790 License. However, in accepting such obligations, You may act only
93791 on Your own behalf and on Your sole responsibility, not on behalf
93792 of any other Contributor, and only if You agree to indemnify,
93793 defend, and hold each Contributor harmless for any liability
93794 incurred by, or claims asserted against, such Contributor by reason
93795 of your accepting any such warranty or additional liability.
93796
93797 END OF TERMS AND CONDITIONS
93798
93799 APPENDIX: How to apply the Apache License to your work.
93800
93801 To apply the Apache License to your work, attach the following
93802 boilerplate notice, with the fields enclosed by brackets "[]"
93803 replaced with your own identifying information. (Don't include
93804 the brackets!) The text should be enclosed in the appropriate
93805 comment syntax for the file format. We also recommend that a
93806 file or class name and description of purpose be included on the
93807 same "printed page" as the copyright notice for easier
93808 identification within third-party archives.
93809
93810 Copyright [yyyy] [name of copyright owner]
93811
93812 Licensed under the Apache License, Version 2.0 (the "License");
93813 you may not use this file except in compliance with the License.
93814 You may obtain a copy of the License at
93815
93816 http://www.apache.org/licenses/LICENSE-2.0
93817
93818 Unless required by applicable law or agreed to in writing, software
93819 distributed under the License is distributed on an "AS IS" BASIS,
93820 WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
93821 See the License for the specific language governing permissions and
93822 limitations under the License.<?php
93823
93824 /*
93825 * Copyright 2012 Johannes M. Schmitt <schmittjoh@gmail.com>
93826 *
93827 * Licensed under the Apache License, Version 2.0 (the "License");
93828 * you may not use this file except in compliance with the License.
93829 * You may obtain a copy of the License at
93830 *
93831 * http://www.apache.org/licenses/LICENSE-2.0
93832 *
93833 * Unless required by applicable law or agreed to in writing, software
93834 * distributed under the License is distributed on an "AS IS" BASIS,
93835 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
93836 * See the License for the specific language governing permissions and
93837 * limitations under the License.
93838 */
93839
93840 namespace JMS\Parser;
93841
93842 /**
93843 * Abstract Lexer.
93844 *
93845 * @author Johannes M. Schmitt <schmittjoh@gmail.com>
93846 */
93847 abstract class AbstractLexer
93848 {
93849 public $token;
93850 public $next;
93851
93852 private $i;
93853 private $peek;
93854 private $tokens;
93855
93856 /**
93857 * Returns the name of the given token.
93858 *
93859 * @param integer $type
93860 *
93861 * @return string
93862 */
93863 public function getName($type)
93864 {
93865 $ref = new \ReflectionClass($this);
93866 foreach ($ref->getConstants() as $name => $value) {
93867 if ($value === $type) {
93868 return $name;
93869 }
93870 }
93871
93872 throw new \InvalidArgumentException(sprintf('There is no token with value %s.', json_encode($type)));
93873 }
93874
93875 public function setInput($str)
93876 {
93877 $tokens = preg_split($this->getRegex(), $str, null, PREG_SPLIT_DELIM_CAPTURE | PREG_SPLIT_NO_EMPTY | PREG_SPLIT_OFFSET_CAPTURE);
93878
93879 $this->tokens = array();
93880 foreach ($tokens as $token) {
93881 list($token[2], $token[0]) = $this->determineTypeAndValue($token[0]);
93882 $this->tokens[] = $token;
93883 }
93884
93885 $this->reset();
93886 }
93887
93888 public function reset()
93889 {
93890 $this->i = -1;
93891 $this->peek = 0;
93892 $this->token = $this->next = null;
93893 $this->moveNext();
93894 }
93895
93896 /**
93897 * Moves the pointer one token forward.
93898 *
93899 * @return boolean if we have not yet reached the end of the input
93900 */
93901 public function moveNext()
93902 {
93903 $this->peek = 0;
93904 $this->token = $this->next;
93905 $this->next = isset($this->tokens[++$this->i]) ? $this->tokens[$this->i] : null;
93906
93907 return null !== $this->next;
93908 }
93909
93910 /**
93911 * Skips the token stream until a token of the given type.
93912 *
93913 * @param integer $type
93914 *
93915 * @return boolean true if a token of the passed type was found, false otherwise.
93916 */
93917 public function skipUntil($type)
93918 {
93919 while ( ! $this->isNext($type) && $this->moveNext());
93920
93921 if ( ! $this->isNext($type)) {
93922 throw new \RuntimeException(sprintf('Could not find the token %s.', $this->getName($type)));
93923 }
93924 }
93925
93926 /**
93927 * @param integer $type
93928 *
93929 * @return boolean
93930 */
93931 public function isNext($type)
93932 {
93933 return null !== $this->next && $type === $this->next[2];
93934 }
93935
93936 /**
93937 * @param array<integer> $types
93938 *
93939 * @return boolean
93940 */
93941 public function isNextAny(array $types)
93942 {
93943 if (null === $this->next) {
93944 return false;
93945 }
93946
93947 foreach ($types as $type) {
93948 if ($type === $this->next[2]) {
93949 return true;
93950 }
93951 }
93952
93953 return false;
93954 }
93955
93956 /**
93957 * @return \PhpOption\Option<[string,integer,integer]>
93958 */
93959 public function peek()
93960 {
93961 if ( ! isset($this->tokens[$this->i + (++$this->peek)])) {
93962 return \PhpOption\None::create();
93963 }
93964
93965 return new \PhpOption\Some($this->tokens[$this->i + $this->peek]);
93966 }
93967
93968 /**
93969 * @return string
93970 */
93971 abstract protected function getRegex();
93972
93973 /**
93974 * Determines the type of the given value.
93975 *
93976 * This method may also modify the passed value for example to cast them to
93977 * a different PHP type where necessary.
93978 *
93979 * @param string $value
93980 *
93981 * @return array a tupel of type and normalized value
93982 */
93983 abstract protected function determineTypeAndValue($value);
93984 }
93985 <?php
93986
93987 /*
93988 * Copyright 2012 Johannes M. Schmitt <schmittjoh@gmail.com>
93989 *
93990 * Licensed under the Apache License, Version 2.0 (the "License");
93991 * you may not use this file except in compliance with the License.
93992 * You may obtain a copy of the License at
93993 *
93994 * http://www.apache.org/licenses/LICENSE-2.0
93995 *
93996 * Unless required by applicable law or agreed to in writing, software
93997 * distributed under the License is distributed on an "AS IS" BASIS,
93998 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
93999 * See the License for the specific language governing permissions and
94000 * limitations under the License.
94001 */
94002
94003 namespace JMS\Parser;
94004
94005 class SyntaxErrorException extends \RuntimeException
94006 {
94007 private $actualToken;
94008 private $context;
94009
94010 public function setActualToken(array $actualToken)
94011 {
94012 $this->actualToken = $actualToken;
94013 }
94014
94015 public function setContext($context)
94016 {
94017 $this->context = $context;
94018 }
94019
94020 public function getActualToken()
94021 {
94022 return $this->actualToken;
94023 }
94024
94025 public function getContext()
94026 {
94027 return $this->context;
94028 }
94029 }
94030 <?php
94031
94032 /*
94033 * Copyright 2012 Johannes M. Schmitt <schmittjoh@gmail.com>
94034 *
94035 * Licensed under the Apache License, Version 2.0 (the "License");
94036 * you may not use this file except in compliance with the License.
94037 * You may obtain a copy of the License at
94038 *
94039 * http://www.apache.org/licenses/LICENSE-2.0
94040 *
94041 * Unless required by applicable law or agreed to in writing, software
94042 * distributed under the License is distributed on an "AS IS" BASIS,
94043 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
94044 * See the License for the specific language governing permissions and
94045 * limitations under the License.
94046 */
94047
94048 namespace JMS\Parser;
94049
94050 /**
94051 * Base Parser which provides some useful parsing methods intended for sub-classing.
94052 *
94053 * @author Johannes M. Schmitt <schmittjoh@gmail.com>
94054 */
94055 abstract class AbstractParser
94056 {
94057 protected $lexer;
94058 protected $context;
94059
94060 public function __construct(AbstractLexer $lexer)
94061 {
94062 $this->lexer = $lexer;
94063 }
94064
94065 /**
94066 * Parses the given input.
94067 *
94068 * @param string $str
94069 * @param string $context parsing context (allows to produce better error messages)
94070 *
94071 * @return mixed
94072 */
94073 public function parse($str, $context = null)
94074 {
94075 $this->lexer->setInput($str);
94076 $this->context = $context;
94077
94078 $rs = $this->parseInternal();
94079
94080 if (null !== $this->lexer->next) {
94081 $this->syntaxError('end of input');
94082 }
94083
94084 return $rs;
94085 }
94086
94087 /**
94088 * @return mixed
94089 */
94090 abstract protected function parseInternal();
94091
94092 /**
94093 * Matches a token, and returns its value.
94094 *
94095 * @param integer $type
94096 *
94097 * @return mixed the value of the matched token
94098 */
94099 protected function match($type)
94100 {
94101 if ( ! $this->lexer->isNext($type)) {
94102 $this->syntaxError($this->lexer->getName($type));
94103 }
94104
94105 $this->lexer->moveNext();
94106
94107 return $this->lexer->token[0];
94108 }
94109
94110 /**
94111 * Matches any of the passed tokens, and returns the matched token's value.
94112 *
94113 * @param array<integer> $types
94114 *
94115 * @return mixed
94116 */
94117 protected function matchAny(array $types)
94118 {
94119 if ( ! $this->lexer->isNextAny($types)) {
94120 $this->syntaxError('any of '.implode(' or ', array_map(array($this->lexer, 'getName'), $types)));
94121 }
94122
94123 $this->lexer->moveNext();
94124
94125 return $this->lexer->token[0];
94126 }
94127
94128 /**
94129 * Raises a syntax error exception.
94130 *
94131 * @param string $expectedDesc A human understandable explanation what was expected
94132 * @param array $actualToken The token that was found. If not given, next token will be assumed.
94133 */
94134 protected function syntaxError($expectedDesc, $actualToken = null)
94135 {
94136 if (null === $actualToken) {
94137 $actualToken = $this->lexer->next;
94138 }
94139 if (null === $actualToken) {
94140 $actualDesc = 'end of input';
94141 } else if ($actualToken[1] === 0) {
94142 $actualDesc = sprintf('"%s" of type %s at beginning of input', $actualToken[0], $this->lexer->getName($actualToken[2]));
94143 } else {
94144 $actualDesc = sprintf('"%s" of type %s at position %d (0-based)', $actualToken[0], $this->lexer->getName($actualToken[2]), $actualToken[1]);
94145 }
94146
94147 $ex = new SyntaxErrorException(sprintf('Expected %s, but got %s%s.', $expectedDesc, $actualDesc, $this->context ? ' '.$this->context : ''));
94148 if (null !== $actualToken) {
94149 $ex->setActualToken($actualToken);
94150 }
94151 if (null !== $this->context) {
94152 $ex->setContext($this->context);
94153 }
94154
94155 throw $ex;
94156 }
94157 }<?php
94158
94159 /*
94160 * Copyright 2012 Johannes M. Schmitt <schmittjoh@gmail.com>
94161 *
94162 * Licensed under the Apache License, Version 2.0 (the "License");
94163 * you may not use this file except in compliance with the License.
94164 * You may obtain a copy of the License at
94165 *
94166 * http://www.apache.org/licenses/LICENSE-2.0
94167 *
94168 * Unless required by applicable law or agreed to in writing, software
94169 * distributed under the License is distributed on an "AS IS" BASIS,
94170 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
94171 * See the License for the specific language governing permissions and
94172 * limitations under the License.
94173 */
94174
94175 namespace JMS\Parser;
94176
94177 /**
94178 * The simple lexer is a fully usable lexer that does not require sub-classing.
94179 *
94180 * @author Johannes M. Schmitt <schmittjoh@gmail.com>
94181 */
94182 class SimpleLexer extends AbstractLexer
94183 {
94184 private $regex;
94185 private $callable;
94186 private $tokenNames;
94187
94188 public function __construct($regex, array $tokenNames, $typeCallable)
94189 {
94190 $this->regex = $regex;
94191 $this->callable = $typeCallable;
94192 $this->tokenNames = $tokenNames;
94193 }
94194
94195 public function getName($type)
94196 {
94197 if ( ! isset($this->tokenNames[$type])) {
94198 throw new \InvalidArgumentException(sprintf('There is no token with type %s.', json_encode($type)));
94199 }
94200
94201 return $this->tokenNames[$type];
94202 }
94203
94204 protected function getRegex()
94205 {
94206 return $this->regex;
94207 }
94208
94209 protected function determineTypeAndValue($value)
94210 {
94211 return call_user_func($this->callable, $value);
94212 }
94213 }{
94214 "hash": "804f267fbd2c33067f6cafd3576f70dd",
94215 "packages": [
94216 {
94217 "name": "phpoption/phpoption",
94218 "version": "0.9.0",
94219 "source": {
94220 "type": "git",
94221 "url": "git://github.com/schmittjoh/php-option",
94222 "reference": "0.9.0"
94223 },
94224 "dist": {
94225 "type": "zip",
94226 "url": "https://github.com/schmittjoh/php-option/archive/0.9.0.zip",
94227 "reference": "0.9.0",
94228 "shasum": ""
94229 },
94230 "require": {
94231 "php": ">=5.3.0"
94232 },
94233 "require-dev": {
94234 "phpunit/phpunit": "3.7.*"
94235 },
94236 "time": "2012-11-12 08:25:35",
94237 "type": "library",
94238 "installation-source": "dist",
94239 "autoload": {
94240 "psr-0": {
94241 "PhpOption\\": "src/"
94242 }
94243 },
94244 "license": [
94245 "Apache2"
94246 ],
94247 "authors": [
94248 {
94249 "name": "Johannes M. Schmitt",
94250 "email": "schmittjoh@gmail.com",
94251 "homepage": "http://jmsyst.com",
94252 "role": "Developer of wrapped JMSSerializerBundle"
94253 }
94254 ],
94255 "description": "Option Type for PHP",
94256 "keywords": [
94257 "php",
94258 "language",
94259 "type",
94260 "option"
94261 ]
94262 }
94263 ],
94264 "packages-dev": [
94265
94266 ],
94267 "aliases": [
94268
94269 ],
94270 "minimum-stability": "stable",
94271 "stability-flags": [
94272
94273 ]
94274 }
94275 THE WORK (AS DEFINED BELOW) IS PROVIDED UNDER THE TERMS OF THIS CREATIVE COMMONS PUBLIC LICENSE ("CCPL" OR "LICENSE"). THE WORK IS PROTECTED BY COPYRIGHT AND/OR OTHER APPLICABLE LAW. ANY USE OF THE WORK OTHER THAN AS AUTHORIZED UNDER THIS LICENSE OR COPYRIGHT LAW IS PROHIBITED.
94276
94277 BY EXERCISING ANY RIGHTS TO THE WORK PROVIDED HERE, YOU ACCEPT AND AGREE TO BE BOUND BY THE TERMS OF THIS LICENSE. TO THE EXTENT THIS LICENSE MAY BE CONSIDERED TO BE A CONTRACT, THE LICENSOR GRANTS YOU THE RIGHTS CONTAINED HERE IN CONSIDERATION OF YOUR ACCEPTANCE OF SUCH TERMS AND CONDITIONS.
94278
94279 1. Definitions
94280
94281 "Adaptation" means a work based upon the Work, or upon the Work and other pre-existing works, such as a translation, adaptation, derivative work, arrangement of music or other alterations of a literary or artistic work, or phonogram or performance and includes cinematographic adaptations or any other form in which the Work may be recast, transformed, or adapted including in any form recognizably derived from the original, except that a work that constitutes a Collection will not be considered an Adaptation for the purpose of this License. For the avoidance of doubt, where the Work is a musical work, performance or phonogram, the synchronization of the Work in timed-relation with a moving image ("synching") will be considered an Adaptation for the purpose of this License.
94282 "Collection" means a collection of literary or artistic works, such as encyclopedias and anthologies, or performances, phonograms or broadcasts, or other works or subject matter other than works listed in Section 1(f) below, which, by reason of the selection and arrangement of their contents, constitute intellectual creations, in which the Work is included in its entirety in unmodified form along with one or more other contributions, each constituting separate and independent works in themselves, which together are assembled into a collective whole. A work that constitutes a Collection will not be considered an Adaptation (as defined above) for the purposes of this License.
94283 "Distribute" means to make available to the public the original and copies of the Work through sale or other transfer of ownership.
94284 "Licensor" means the individual, individuals, entity or entities that offer(s) the Work under the terms of this License.
94285 "Original Author" means, in the case of a literary or artistic work, the individual, individuals, entity or entities who created the Work or if no individual or entity can be identified, the publisher; and in addition (i) in the case of a performance the actors, singers, musicians, dancers, and other persons who act, sing, deliver, declaim, play in, interpret or otherwise perform literary or artistic works or expressions of folklore; (ii) in the case of a phonogram the producer being the person or legal entity who first fixes the sounds of a performance or other sounds; and, (iii) in the case of broadcasts, the organization that transmits the broadcast.
94286 "Work" means the literary and/or artistic work offered under the terms of this License including without limitation any production in the literary, scientific and artistic domain, whatever may be the mode or form of its expression including digital form, such as a book, pamphlet and other writing; a lecture, address, sermon or other work of the same nature; a dramatic or dramatico-musical work; a choreographic work or entertainment in dumb show; a musical composition with or without words; a cinematographic work to which are assimilated works expressed by a process analogous to cinematography; a work of drawing, painting, architecture, sculpture, engraving or lithography; a photographic work to which are assimilated works expressed by a process analogous to photography; a work of applied art; an illustration, map, plan, sketch or three-dimensional work relative to geography, topography, architecture or science; a performance; a broadcast; a phonogram; a compilation of data to the extent it is protected as a copyrightable work; or a work performed by a variety or circus performer to the extent it is not otherwise considered a literary or artistic work.
94287 "You" means an individual or entity exercising rights under this License who has not previously violated the terms of this License with respect to the Work, or who has received express permission from the Licensor to exercise rights under this License despite a previous violation.
94288 "Publicly Perform" means to perform public recitations of the Work and to communicate to the public those public recitations, by any means or process, including by wire or wireless means or public digital performances; to make available to the public Works in such a way that members of the public may access these Works from a place and at a place individually chosen by them; to perform the Work to the public by any means or process and the communication to the public of the performances of the Work, including by public digital performance; to broadcast and rebroadcast the Work by any means including signs, sounds or images.
94289 "Reproduce" means to make copies of the Work by any means including without limitation by sound or visual recordings and the right of fixation and reproducing fixations of the Work, including storage of a protected performance or phonogram in digital form or other electronic medium.
94290
94291 2. Fair Dealing Rights. Nothing in this License is intended to reduce, limit, or restrict any uses free from copyright or rights arising from limitations or exceptions that are provided for in connection with the copyright protection under copyright law or other applicable laws.
94292
94293 3. License Grant. Subject to the terms and conditions of this License, Licensor hereby grants You a worldwide, royalty-free, non-exclusive, perpetual (for the duration of the applicable copyright) license to exercise the rights in the Work as stated below:
94294
94295 to Reproduce the Work, to incorporate the Work into one or more Collections, and to Reproduce the Work as incorporated in the Collections; and,
94296 to Distribute and Publicly Perform the Work including as incorporated in Collections.
94297
94298 The above rights may be exercised in all media and formats whether now known or hereafter devised. The above rights include the right to make such modifications as are technically necessary to exercise the rights in other media and formats, but otherwise you have no rights to make Adaptations. Subject to 8(f), all rights not expressly granted by Licensor are hereby reserved, including but not limited to the rights set forth in Section 4(d).
94299
94300 4. Restrictions. The license granted in Section 3 above is expressly made subject to and limited by the following restrictions:
94301
94302 You may Distribute or Publicly Perform the Work only under the terms of this License. You must include a copy of, or the Uniform Resource Identifier (URI) for, this License with every copy of the Work You Distribute or Publicly Perform. You may not offer or impose any terms on the Work that restrict the terms of this License or the ability of the recipient of the Work to exercise the rights granted to that recipient under the terms of the License. You may not sublicense the Work. You must keep intact all notices that refer to this License and to the disclaimer of warranties with every copy of the Work You Distribute or Publicly Perform. When You Distribute or Publicly Perform the Work, You may not impose any effective technological measures on the Work that restrict the ability of a recipient of the Work from You to exercise the rights granted to that recipient under the terms of the License. This Section 4(a) applies to the Work as incorporated in a Collection, but this does not require the Collection apart from the Work itself to be made subject to the terms of this License. If You create a Collection, upon notice from any Licensor You must, to the extent practicable, remove from the Collection any credit as required by Section 4(c), as requested.
94303 You may not exercise any of the rights granted to You in Section 3 above in any manner that is primarily intended for or directed toward commercial advantage or private monetary compensation. The exchange of the Work for other copyrighted works by means of digital file-sharing or otherwise shall not be considered to be intended for or directed toward commercial advantage or private monetary compensation, provided there is no payment of any monetary compensation in connection with the exchange of copyrighted works.
94304 If You Distribute, or Publicly Perform the Work or Collections, You must, unless a request has been made pursuant to Section 4(a), keep intact all copyright notices for the Work and provide, reasonable to the medium or means You are utilizing: (i) the name of the Original Author (or pseudonym, if applicable) if supplied, and/or if the Original Author and/or Licensor designate another party or parties (e.g., a sponsor institute, publishing entity, journal) for attribution ("Attribution Parties") in Licensor's copyright notice, terms of service or by other reasonable means, the name of such party or parties; (ii) the title of the Work if supplied; (iii) to the extent reasonably practicable, the URI, if any, that Licensor specifies to be associated with the Work, unless such URI does not refer to the copyright notice or licensing information for the Work. The credit required by this Section 4(c) may be implemented in any reasonable manner; provided, however, that in the case of a Collection, at a minimum such credit will appear, if a credit for all contributing authors of Collection appears, then as part of these credits and in a manner at least as prominent as the credits for the other contributing authors. For the avoidance of doubt, You may only use the credit required by this Section for the purpose of attribution in the manner set out above and, by exercising Your rights under this License, You may not implicitly or explicitly assert or imply any connection with, sponsorship or endorsement by the Original Author, Licensor and/or Attribution Parties, as appropriate, of You or Your use of the Work, without the separate, express prior written permission of the Original Author, Licensor and/or Attribution Parties.
94305
94306 For the avoidance of doubt:
94307 Non-waivable Compulsory License Schemes. In those jurisdictions in which the right to collect royalties through any statutory or compulsory licensing scheme cannot be waived, the Licensor reserves the exclusive right to collect such royalties for any exercise by You of the rights granted under this License;
94308 Waivable Compulsory License Schemes. In those jurisdictions in which the right to collect royalties through any statutory or compulsory licensing scheme can be waived, the Licensor reserves the exclusive right to collect such royalties for any exercise by You of the rights granted under this License if Your exercise of such rights is for a purpose or use which is otherwise than noncommercial as permitted under Section 4(b) and otherwise waives the right to collect royalties through any statutory or compulsory licensing scheme; and,
94309 Voluntary License Schemes. The Licensor reserves the right to collect royalties, whether individually or, in the event that the Licensor is a member of a collecting society that administers voluntary licensing schemes, via that society, from any exercise by You of the rights granted under this License that is for a purpose or use which is otherwise than noncommercial as permitted under Section 4(b).
94310 Except as otherwise agreed in writing by the Licensor or as may be otherwise permitted by applicable law, if You Reproduce, Distribute or Publicly Perform the Work either by itself or as part of any Collections, You must not distort, mutilate, modify or take other derogatory action in relation to the Work which would be prejudicial to the Original Author's honor or reputation.
94311
94312 5. Representations, Warranties and Disclaimer
94313
94314 UNLESS OTHERWISE MUTUALLY AGREED BY THE PARTIES IN WRITING, LICENSOR OFFERS THE WORK AS-IS AND MAKES NO REPRESENTATIONS OR WARRANTIES OF ANY KIND CONCERNING THE WORK, EXPRESS, IMPLIED, STATUTORY OR OTHERWISE, INCLUDING, WITHOUT LIMITATION, WARRANTIES OF TITLE, MERCHANTIBILITY, FITNESS FOR A PARTICULAR PURPOSE, NONINFRINGEMENT, OR THE ABSENCE OF LATENT OR OTHER DEFECTS, ACCURACY, OR THE PRESENCE OF ABSENCE OF ERRORS, WHETHER OR NOT DISCOVERABLE. SOME JURISDICTIONS DO NOT ALLOW THE EXCLUSION OF IMPLIED WARRANTIES, SO SUCH EXCLUSION MAY NOT APPLY TO YOU.
94315
94316 6. Limitation on Liability. EXCEPT TO THE EXTENT REQUIRED BY APPLICABLE LAW, IN NO EVENT WILL LICENSOR BE LIABLE TO YOU ON ANY LEGAL THEORY FOR ANY SPECIAL, INCIDENTAL, CONSEQUENTIAL, PUNITIVE OR EXEMPLARY DAMAGES ARISING OUT OF THIS LICENSE OR THE USE OF THE WORK, EVEN IF LICENSOR HAS BEEN ADVISED OF THE POSSIBILITY OF SUCH DAMAGES.
94317
94318 7. Termination
94319
94320 This License and the rights granted hereunder will terminate automatically upon any breach by You of the terms of this License. Individuals or entities who have received Collections from You under this License, however, will not have their licenses terminated provided such individuals or entities remain in full compliance with those licenses. Sections 1, 2, 5, 6, 7, and 8 will survive any termination of this License.
94321 Subject to the above terms and conditions, the license granted here is perpetual (for the duration of the applicable copyright in the Work). Notwithstanding the above, Licensor reserves the right to release the Work under different license terms or to stop distributing the Work at any time; provided, however that any such election will not serve to withdraw this License (or any other license that has been, or is required to be, granted under the terms of this License), and this License will continue in full force and effect unless terminated as stated above.
94322
94323 8. Miscellaneous
94324
94325 Each time You Distribute or Publicly Perform the Work or a Collection, the Licensor offers to the recipient a license to the Work on the same terms and conditions as the license granted to You under this License.
94326 If any provision of this License is invalid or unenforceable under applicable law, it shall not affect the validity or enforceability of the remainder of the terms of this License, and without further action by the parties to this agreement, such provision shall be reformed to the minimum extent necessary to make such provision valid and enforceable.
94327 No term or provision of this License shall be deemed waived and no breach consented to unless such waiver or consent shall be in writing and signed by the party to be charged with such waiver or consent.
94328 This License constitutes the entire agreement between the parties with respect to the Work licensed here. There are no understandings, agreements or representations with respect to the Work not specified here. Licensor shall not be bound by any additional provisions that may appear in any communication from You. This License may not be modified without the mutual written agreement of the Licensor and You.
94329 The rights granted under, and the subject matter referenced, in this License were drafted utilizing the terminology of the Berne Convention for the Protection of Literary and Artistic Works (as amended on September 28, 1979), the Rome Convention of 1961, the WIPO Copyright Treaty of 1996, the WIPO Performances and Phonograms Treaty of 1996 and the Universal Copyright Convention (as revised on July 24, 1971). These rights and subject matter take effect in the relevant jurisdiction in which the License terms are sought to be enforced according to the corresponding provisions of the implementation of those treaty provisions in the applicable national law. If the standard suite of rights granted under applicable copyright law includes additional rights not granted under this License, such additional rights are deemed to be included in the License; this License is not intended to restrict the license of any rights under applicable law.
94330 Parser Library
94331 ==============
94332
94333 This library allows you to easily implement recursive-descent parsers.
94334
94335 Installation
94336 ------------
94337 You can install this library through composer:
94338
94339 .. code-block :: bash
94340
94341 composer require jms/parser-lib
94342
94343 or add it to your ``composer.json`` file directly.
94344
94345 Example
94346 -------
94347 Let's assume that you would like to write a parser for a calculator. For simplicity
94348 sake, we will assume that the parser would already return the result of the
94349 calculation. Inputs could look like this ``1 + 1`` and we would expect ``2`` as
94350 a result.
94351
94352 The first step, is to create a lexer which breaks the input string up into
94353 individual tokens which can then be consumed by the parser. This library provides
94354 a convenient class for simple problems which we will use::
94355
94356 $lexer = new \JMS\Parser\SimpleLexer(
94357 '/
94358 # Numbers
94359 ([0-9]+)
94360
94361 # Do not surround with () because whitespace is not meaningful for
94362 # our purposes.
94363 |\s+
94364
94365 # Operators; we support only + and -
94366 |(+)|(-)
94367 /x', // The x modifier tells PCRE to ignore whitespace in the regex above.
94368
94369 // This maps token types to a human readable name.
94370 array(0 => 'T_UNKNOWN', 1 => 'T_INT', 2 => 'T_PLUS', 3 => 'T_MINUS'),
94371
94372 // This function tells the lexer which type a token has. The first element is
94373 // an integer from the map above, the second element the normalized value.
94374 function($value) {
94375 if ('+' === $value) {
94376 return array(2, '+');
94377 }
94378 if ('-' === $value) {
94379 return array(3, '-');
94380 }
94381 if (is_numeric($value)) {
94382 return array(1, (integer) $value);
94383 }
94384
94385 return array(0, $value);
94386 }
94387 );
94388
94389 Now the second step, is to create the parser which can consume the tokens once
94390 the lexer has split them::
94391
94392 class MyParser extends \JMS\Parser\AbstractParser
94393 {
94394 const T_UNKNOWN = 0;
94395 const T_INT = 1;
94396 const T_PLUS = 2;
94397 const T_MINUS = 3;
94398
94399 public function parseInternal()
94400 {
94401 $result = $this->match(self::T_INT);
94402
94403 while ($this->lexer->isNextAny(array(self::T_PLUS, self::T_MINUS))) {
94404 if ($this->lexer->isNext(self::T_PLUS)) {
94405 $this->lexer->moveNext();
94406 $result += $this->match(self::T_INT);
94407 } else if ($this->lexer->isNext(self::T_MINUS)) {
94408 $this->lexer->moveNext();
94409 $result -= $this->match(self::T_INT);
94410 } else {
94411 throw new \LogicException('Previous ifs were exhaustive.');
94412 }
94413 }
94414
94415 return $result;
94416 }
94417 }
94418
94419 $parser = new MyParser($lexer);
94420 $parser->parse('1 + 1'); // int(2)
94421 $parser->parse('5 + 10 - 4'); // int(11)
94422
94423 That's it. Now you can perform basic operations already. If you like you can now
94424 also replace the hard-coded integers in the lexer with the class constants of the
94425 parser.
94426
94427 License
94428 -------
94429
94430 The code is released under the business-friendly `Apache2 license`_.
94431
94432 Documentation is subject to the `Attribution-NonCommercial-NoDerivs 3.0 Unported
94433 license`_.
94434
94435 .. _Apache2 license: http://www.apache.org/licenses/LICENSE-2.0.html
94436 .. _Attribution-NonCommercial-NoDerivs 3.0 Unported license: http://creativecommons.org/licenses/by-nc-nd/3.0/
94437
94438 <?php
94439
94440 if ( ! is_file($autoloadFile = __DIR__.'/../vendor/autoload.php')) {
94441 echo 'Could not find "vendor/autoload.php". Did you forget to run "composer install --dev"?'.PHP_EOL;
94442 exit(1);
94443 }
94444
94445 require_once $autoloadFile;<?php
94446
94447 namespace JMS\Parser;
94448
94449 class AbstractLexerTest extends \PHPUnit_Framework_TestCase
94450 {
94451 const T_UNKNOWN = 0;
94452 const T_STRING = 1;
94453 const T_INTEGER = 2;
94454
94455 const T_COMMA = 100;
94456
94457 private $lexer;
94458
94459 public function testTokenization()
94460 {
94461 $this->lexer->setInput('"foo" 1234');
94462
94463 $this->assertNull($this->lexer->token);
94464 $this->assertNotNull($this->lexer->next);
94465
94466 $this->assertAttributeEquals(array(
94467 array('foo', 0, self::T_STRING),
94468 array(1234, 7, self::T_INTEGER),
94469 ), 'tokens', $this->lexer);
94470 }
94471
94472 public function testMoveNext()
94473 {
94474 $this->lexer->setInput('1 2 3');
94475 $this->assertNull($this->lexer->token);
94476
94477 $this->assertTrue($this->lexer->moveNext());
94478 $this->assertValue(1, $this->lexer->token);
94479
94480 $this->assertTrue($this->lexer->moveNext());
94481 $this->assertValue(2, $this->lexer->token);
94482
94483 $this->assertFalse($this->lexer->moveNext());
94484 $this->assertValue(3, $this->lexer->token);
94485 }
94486
94487 /**
94488 * @expectedException \InvalidArgumentException
94489 */
94490 public function testSkipUntilWithNonExistent()
94491 {
94492 $this->lexer->setInput('1 2 3');
94493 $this->lexer->skipUntil(self::T_STRING);
94494 }
94495
94496 public function testSkipUntil()
94497 {
94498 $this->lexer->setInput('1 "foo"');
94499 $this->assertNull($this->lexer->skipUntil(self::T_STRING));
94500 $this->assertValue(1, $this->lexer->token);
94501 $this->assertValue('foo', $this->lexer->next);
94502 }
94503
94504 public function testIsNext()
94505 {
94506 $this->lexer->setInput('1');
94507 $this->assertTrue($this->lexer->isNext(self::T_INTEGER));
94508 $this->assertFalse($this->lexer->isNext(self::T_COMMA));
94509 }
94510
94511 public function testIsNextAny()
94512 {
94513 $this->lexer->setInput('1');
94514 $this->assertTrue($this->lexer->isNextAny(array(self::T_COMMA, self::T_INTEGER)));
94515 $this->assertFalse($this->lexer->isNextAny(array(self::T_COMMA, self::T_STRING)));
94516 }
94517
94518 public function testPeek()
94519 {
94520 $this->lexer->setInput('1 2 3');
94521
94522 $this->assertValue(1, $this->lexer->next);
94523 $this->assertValue(2, $this->lexer->peek()->get());
94524
94525 $this->assertValue(1, $this->lexer->next);
94526 $this->assertValue(3, $this->lexer->peek()->get());
94527
94528 $this->assertValue(1, $this->lexer->next);
94529 $this->assertTrue($this->lexer->moveNext());
94530 $this->assertValue(2, $this->lexer->next);
94531 $this->assertValue(3, $this->lexer->peek()->get());
94532 $this->assertValue(2, $this->lexer->next);
94533 }
94534
94535 private function assertValue($expected, $actualToken)
94536 {
94537 $this->assertNotNull($actualToken);
94538 $this->assertSame($expected, $actualToken[0]);
94539 }
94540
94541 protected function setUp()
94542 {
94543 $this->lexer = $this->getMockForAbstractClass('JMS\Parser\AbstractLexer');
94544 $this->lexer->expects($this->any())
94545 ->method('getRegex')
94546 ->will($this->returnValue('/("(?:[^"]*|(?<=\\)"))*")|([0-9]+)|\s+|(.)/i'));
94547 $this->lexer->expects($this->any())
94548 ->method('determineTypeAndValue')
94549 ->will($this->returnCallback(function($value) {
94550 if (',' === $value) {
94551 return array(AbstractLexerTest::T_COMMA, $value);
94552 }
94553
94554 if ('"' === $value[0]) {
94555 return array(AbstractLexerTest::T_STRING, substr($value, 1, -1));
94556 }
94557
94558 if (preg_match('/^[0-9]+$/', $value)) {
94559 return array(AbstractLexerTest::T_INTEGER, (integer) $value);
94560 }
94561
94562 return array(AbstractLexerTest::T_UNKNOWN, $value);
94563 }));
94564 }
94565 }
94566 <?php
94567
94568 namespace JMS\Parser\Tests;
94569
94570 class AbstractParserTest extends \PHPUnit_Framework_TestCase
94571 {
94572 const T_UNKNOWN = 0;
94573 const T_INT = 1;
94574 const T_PLUS = 100;
94575 const T_MINUS = 101;
94576
94577 private $parser;
94578 private $lexer;
94579
94580 public function testParse()
94581 {
94582 $this->assertSame(2, $this->parser->parse('1 + 1'));
94583 $this->assertSame(5, $this->parser->parse('1 + 1 + 4 - 1'));
94584 }
94585
94586 /**
94587 * @expectedException JMS\Parser\SyntaxErrorException
94588 * @expectedExceptionMessage Expected T_INT, but got end of input.
94589 */
94590 public function testUnexpectedEnd()
94591 {
94592 $this->parser->parse('1 + ');
94593 }
94594
94595 protected function setUp()
94596 {
94597 $this->lexer = $lexer = new \JMS\Parser\SimpleLexer(
94598 '/([0-9]+)|\s+|(.)/',
94599 array(0 => 'T_UNKNOWN', 1 => 'T_INT', 100 => 'T_PLUS', 101 => 'T_MINUS'),
94600 function($value) {
94601 if ('+' === $value) {
94602 return array(AbstractParserTest::T_PLUS, $value);
94603 }
94604 if ('-' === $value) {
94605 return array(AbstractParserTest::T_MINUS, $value);
94606 }
94607
94608 // We would loose information on doubles here, but for this test it
94609 // does not matter anyway.
94610 if (is_numeric($value)) {
94611 return array(AbstractParserTest::T_INT, (integer) $value);
94612 }
94613
94614 return AbstractParserTest::T_UNKNOWN;
94615 }
94616 );
94617
94618 $this->parser = $parser = $this->getMockBuilder('JMS\Parser\AbstractParser')
94619 ->setConstructorArgs(array($this->lexer))
94620 ->getMockForAbstractClass();
94621
94622 $match = function($type) use ($parser) {
94623 $ref = new \ReflectionMethod($parser, 'match');
94624 $ref->setAccessible(true);
94625
94626 return $ref->invoke($parser, $type);
94627 };
94628
94629 $this->parser->expects($this->any())
94630 ->method('parseInternal')
94631 ->will($this->returnCallback(function() use ($lexer, $match) {
94632 // Result :== Number ( ("+"|"-") Number )*
94633
94634 $result = $match(AbstractParserTest::T_INT);
94635 while ($lexer->isNextAny(array(AbstractParserTest::T_PLUS, AbstractParserTest::T_MINUS))) {
94636 if ($lexer->isNext(AbstractParserTest::T_PLUS)) {
94637 $lexer->moveNext();
94638
94639 $result += $match(AbstractParserTest::T_INT);
94640 } else if ($lexer->isNext(AbstractParserTest::T_MINUS)) {
94641 $lexer->moveNext();
94642
94643 $result -= $match(AbstractParserTest::T_INT);
94644 } else {
94645 throw new \LogicException('Previous ifs were exhaustive.');
94646 }
94647 }
94648
94649 return $result;
94650 }));
94651 }
94652 }{
94653 "name": "jms/parser-lib",
94654 "description": "A library for easily creating recursive-descent parsers.",
94655 "license": "Apache2",
94656 "require": {
94657 "phpoption/phpoption": ">=0.9,<2.0-dev"
94658 },
94659 "autoload": {
94660 "psr-0": {
94661 "JMS\\": "src/"
94662 }
94663 },
94664 "extra": {
94665 "branch-alias": {
94666 "dev-master": "1.0-dev"
94667 }
94668 }
94669 }<?xml version="1.0" encoding="UTF-8"?>
94670
94671 <phpunit backupGlobals="false"
94672 backupStaticAttributes="false"
94673 colors="true"
94674 convertErrorsToExceptions="true"
94675 convertNoticesToExceptions="true"
94676 convertWarningsToExceptions="true"
94677 processIsolation="false"
94678 stopOnFailure="false"
94679 syntaxCheck="false"
94680 bootstrap="tests/bootstrap.php"
94681 >
94682 <testsuites>
94683 <testsuite name="Parser-Lib Test Suite">
94684 <directory>./tests/JMS/</directory>
94685 </testsuite>
94686 </testsuites>
94687
94688 <groups>
94689 <exclude>
94690 <group>performance</group>
94691 </exclude>
94692 </groups>
94693 </phpunit>
94694 Serializer [![Scrutinizer Quality Score](https://scrutinizer-ci.com/g/schmittjoh/serializer/badges/quality-score.png?s=189df68e00c75d3fe155bc0da0b53b53709a9895)](https://scrutinizer-ci.com/g/schmittjoh/serializer/)
94695 ==========
94696
94697 Learn more about it in its [documentation](http://jmsyst.com/libs/serializer).
94698 Apache License
94699 Version 2.0, January 2004
94700 http://www.apache.org/licenses/
94701
94702 TERMS AND CONDITIONS FOR USE, REPRODUCTION, AND DISTRIBUTION
94703
94704 1. Definitions.
94705
94706 "License" shall mean the terms and conditions for use, reproduction,
94707 and distribution as defined by Sections 1 through 9 of this document.
94708
94709 "Licensor" shall mean the copyright owner or entity authorized by
94710 the copyright owner that is granting the License.
94711
94712 "Legal Entity" shall mean the union of the acting entity and all
94713 other entities that control, are controlled by, or are under common
94714 control with that entity. For the purposes of this definition,
94715 "control" means (i) the power, direct or indirect, to cause the
94716 direction or management of such entity, whether by contract or
94717 otherwise, or (ii) ownership of fifty percent (50%) or more of the
94718 outstanding shares, or (iii) beneficial ownership of such entity.
94719
94720 "You" (or "Your") shall mean an individual or Legal Entity
94721 exercising permissions granted by this License.
94722
94723 "Source" form shall mean the preferred form for making modifications,
94724 including but not limited to software source code, documentation
94725 source, and configuration files.
94726
94727 "Object" form shall mean any form resulting from mechanical
94728 transformation or translation of a Source form, including but
94729 not limited to compiled object code, generated documentation,
94730 and conversions to other media types.
94731
94732 "Work" shall mean the work of authorship, whether in Source or
94733 Object form, made available under the License, as indicated by a
94734 copyright notice that is included in or attached to the work
94735 (an example is provided in the Appendix below).
94736
94737 "Derivative Works" shall mean any work, whether in Source or Object
94738 form, that is based on (or derived from) the Work and for which the
94739 editorial revisions, annotations, elaborations, or other modifications
94740 represent, as a whole, an original work of authorship. For the purposes
94741 of this License, Derivative Works shall not include works that remain
94742 separable from, or merely link (or bind by name) to the interfaces of,
94743 the Work and Derivative Works thereof.
94744
94745 "Contribution" shall mean any work of authorship, including
94746 the original version of the Work and any modifications or additions
94747 to that Work or Derivative Works thereof, that is intentionally
94748 submitted to Licensor for inclusion in the Work by the copyright owner
94749 or by an individual or Legal Entity authorized to submit on behalf of
94750 the copyright owner. For the purposes of this definition, "submitted"
94751 means any form of electronic, verbal, or written communication sent
94752 to the Licensor or its representatives, including but not limited to
94753 communication on electronic mailing lists, source code control systems,
94754 and issue tracking systems that are managed by, or on behalf of, the
94755 Licensor for the purpose of discussing and improving the Work, but
94756 excluding communication that is conspicuously marked or otherwise
94757 designated in writing by the copyright owner as "Not a Contribution."
94758
94759 "Contributor" shall mean Licensor and any individual or Legal Entity
94760 on behalf of whom a Contribution has been received by Licensor and
94761 subsequently incorporated within the Work.
94762
94763 2. Grant of Copyright License. Subject to the terms and conditions of
94764 this License, each Contributor hereby grants to You a perpetual,
94765 worldwide, non-exclusive, no-charge, royalty-free, irrevocable
94766 copyright license to reproduce, prepare Derivative Works of,
94767 publicly display, publicly perform, sublicense, and distribute the
94768 Work and such Derivative Works in Source or Object form.
94769
94770 3. Grant of Patent License. Subject to the terms and conditions of
94771 this License, each Contributor hereby grants to You a perpetual,
94772 worldwide, non-exclusive, no-charge, royalty-free, irrevocable
94773 (except as stated in this section) patent license to make, have made,
94774 use, offer to sell, sell, import, and otherwise transfer the Work,
94775 where such license applies only to those patent claims licensable
94776 by such Contributor that are necessarily infringed by their
94777 Contribution(s) alone or by combination of their Contribution(s)
94778 with the Work to which such Contribution(s) was submitted. If You
94779 institute patent litigation against any entity (including a
94780 cross-claim or counterclaim in a lawsuit) alleging that the Work
94781 or a Contribution incorporated within the Work constitutes direct
94782 or contributory patent infringement, then any patent licenses
94783 granted to You under this License for that Work shall terminate
94784 as of the date such litigation is filed.
94785
94786 4. Redistribution. You may reproduce and distribute copies of the
94787 Work or Derivative Works thereof in any medium, with or without
94788 modifications, and in Source or Object form, provided that You
94789 meet the following conditions:
94790
94791 (a) You must give any other recipients of the Work or
94792 Derivative Works a copy of this License; and
94793
94794 (b) You must cause any modified files to carry prominent notices
94795 stating that You changed the files; and
94796
94797 (c) You must retain, in the Source form of any Derivative Works
94798 that You distribute, all copyright, patent, trademark, and
94799 attribution notices from the Source form of the Work,
94800 excluding those notices that do not pertain to any part of
94801 the Derivative Works; and
94802
94803 (d) If the Work includes a "NOTICE" text file as part of its
94804 distribution, then any Derivative Works that You distribute must
94805 include a readable copy of the attribution notices contained
94806 within such NOTICE file, excluding those notices that do not
94807 pertain to any part of the Derivative Works, in at least one
94808 of the following places: within a NOTICE text file distributed
94809 as part of the Derivative Works; within the Source form or
94810 documentation, if provided along with the Derivative Works; or,
94811 within a display generated by the Derivative Works, if and
94812 wherever such third-party notices normally appear. The contents
94813 of the NOTICE file are for informational purposes only and
94814 do not modify the License. You may add Your own attribution
94815 notices within Derivative Works that You distribute, alongside
94816 or as an addendum to the NOTICE text from the Work, provided
94817 that such additional attribution notices cannot be construed
94818 as modifying the License.
94819
94820 You may add Your own copyright statement to Your modifications and
94821 may provide additional or different license terms and conditions
94822 for use, reproduction, or distribution of Your modifications, or
94823 for any such Derivative Works as a whole, provided Your use,
94824 reproduction, and distribution of the Work otherwise complies with
94825 the conditions stated in this License.
94826
94827 5. Submission of Contributions. Unless You explicitly state otherwise,
94828 any Contribution intentionally submitted for inclusion in the Work
94829 by You to the Licensor shall be under the terms and conditions of
94830 this License, without any additional terms or conditions.
94831 Notwithstanding the above, nothing herein shall supersede or modify
94832 the terms of any separate license agreement you may have executed
94833 with Licensor regarding such Contributions.
94834
94835 6. Trademarks. This License does not grant permission to use the trade
94836 names, trademarks, service marks, or product names of the Licensor,
94837 except as required for reasonable and customary use in describing the
94838 origin of the Work and reproducing the content of the NOTICE file.
94839
94840 7. Disclaimer of Warranty. Unless required by applicable law or
94841 agreed to in writing, Licensor provides the Work (and each
94842 Contributor provides its Contributions) on an "AS IS" BASIS,
94843 WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or
94844 implied, including, without limitation, any warranties or conditions
94845 of TITLE, NON-INFRINGEMENT, MERCHANTABILITY, or FITNESS FOR A
94846 PARTICULAR PURPOSE. You are solely responsible for determining the
94847 appropriateness of using or redistributing the Work and assume any
94848 risks associated with Your exercise of permissions under this License.
94849
94850 8. Limitation of Liability. In no event and under no legal theory,
94851 whether in tort (including negligence), contract, or otherwise,
94852 unless required by applicable law (such as deliberate and grossly
94853 negligent acts) or agreed to in writing, shall any Contributor be
94854 liable to You for damages, including any direct, indirect, special,
94855 incidental, or consequential damages of any character arising as a
94856 result of this License or out of the use or inability to use the
94857 Work (including but not limited to damages for loss of goodwill,
94858 work stoppage, computer failure or malfunction, or any and all
94859 other commercial damages or losses), even if such Contributor
94860 has been advised of the possibility of such damages.
94861
94862 9. Accepting Warranty or Additional Liability. While redistributing
94863 the Work or Derivative Works thereof, You may choose to offer,
94864 and charge a fee for, acceptance of support, warranty, indemnity,
94865 or other liability obligations and/or rights consistent with this
94866 License. However, in accepting such obligations, You may act only
94867 on Your own behalf and on Your sole responsibility, not on behalf
94868 of any other Contributor, and only if You agree to indemnify,
94869 defend, and hold each Contributor harmless for any liability
94870 incurred by, or claims asserted against, such Contributor by reason
94871 of your accepting any such warranty or additional liability.
94872
94873 END OF TERMS AND CONDITIONS
94874
94875 APPENDIX: How to apply the Apache License to your work.
94876
94877 To apply the Apache License to your work, attach the following
94878 boilerplate notice, with the fields enclosed by brackets "[]"
94879 replaced with your own identifying information. (Don't include
94880 the brackets!) The text should be enclosed in the appropriate
94881 comment syntax for the file format. We also recommend that a
94882 file or class name and description of purpose be included on the
94883 same "printed page" as the copyright notice for easier
94884 identification within third-party archives.
94885
94886 Copyright [yyyy] [name of copyright owner]
94887
94888 Licensed under the Apache License, Version 2.0 (the "License");
94889 you may not use this file except in compliance with the License.
94890 You may obtain a copy of the License at
94891
94892 http://www.apache.org/licenses/LICENSE-2.0
94893
94894 Unless required by applicable law or agreed to in writing, software
94895 distributed under the License is distributed on an "AS IS" BASIS,
94896 WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
94897 See the License for the specific language governing permissions and
94898 limitations under the License.<?php
94899
94900 /*
94901 * Copyright 2013 Johannes M. Schmitt <schmittjoh@gmail.com>
94902 *
94903 * Licensed under the Apache License, Version 2.0 (the "License");
94904 * you may not use this file except in compliance with the License.
94905 * You may obtain a copy of the License at
94906 *
94907 * http://www.apache.org/licenses/LICENSE-2.0
94908 *
94909 * Unless required by applicable law or agreed to in writing, software
94910 * distributed under the License is distributed on an "AS IS" BASIS,
94911 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
94912 * See the License for the specific language governing permissions and
94913 * limitations under the License.
94914 */
94915
94916 namespace JMS\Serializer\Handler;
94917
94918 use JMS\Serializer\YamlSerializationVisitor;
94919 use JMS\Serializer\JsonSerializationVisitor;
94920 use JMS\Serializer\GraphNavigator;
94921 use JMS\Serializer\GenericSerializationVisitor;
94922 use Symfony\Component\Form\Form;
94923 use Symfony\Component\Form\FormError;
94924 use Symfony\Component\Translation\TranslatorInterface;
94925 use JMS\Serializer\XmlSerializationVisitor;
94926
94927 class FormErrorHandler implements SubscribingHandlerInterface
94928 {
94929 private $translator;
94930
94931 public static function getSubscribingMethods()
94932 {
94933 $methods = array();
94934 foreach (array('xml', 'json', 'yml') as $format) {
94935 $methods[] = array(
94936 'direction' => GraphNavigator::DIRECTION_SERIALIZATION,
94937 'type' => 'Symfony\Component\Form\Form',
94938 'format' => $format,
94939 );
94940 $methods[] = array(
94941 'direction' => GraphNavigator::DIRECTION_SERIALIZATION,
94942 'type' => 'Symfony\Component\Form\FormError',
94943 'format' => $format,
94944 );
94945 }
94946
94947 return $methods;
94948 }
94949
94950 public function __construct(TranslatorInterface $translator)
94951 {
94952 $this->translator = $translator;
94953 }
94954
94955 public function serializeFormToXml(XmlSerializationVisitor $visitor, Form $form, array $type)
94956 {
94957 if (null === $visitor->document) {
94958 $visitor->document = $visitor->createDocument(null, null, false);
94959 $visitor->document->appendChild($formNode = $visitor->document->createElement('form'));
94960 $visitor->setCurrentNode($formNode);
94961 } else {
94962 $visitor->getCurrentNode()->appendChild(
94963 $formNode = $visitor->document->createElement('form')
94964 );
94965 }
94966
94967 $formNode->setAttribute('name', $form->getName());
94968
94969 $formNode->appendChild($errorsNode = $visitor->document->createElement('errors'));
94970 foreach ($form->getErrors() as $error) {
94971 $errorNode = $visitor->document->createElement('entry');
94972 $errorNode->appendChild($this->serializeFormErrorToXml($visitor, $error, array()));
94973 $errorsNode->appendChild($errorNode);
94974 }
94975
94976 foreach ($form->all() as $child) {
94977 if (null !== $node = $this->serializeFormToXml($visitor, $child, array())) {
94978 $formNode->appendChild($node);
94979 }
94980 }
94981
94982 return $formNode;
94983 }
94984
94985 public function serializeFormToJson(JsonSerializationVisitor $visitor, Form $form, array $type)
94986 {
94987 return $this->convertFormToArray($visitor, $form);
94988 }
94989
94990 public function serializeFormToYml(YamlSerializationVisitor $visitor, Form $form, array $type)
94991 {
94992 return $this->convertFormToArray($visitor, $form);
94993 }
94994
94995 public function serializeFormErrorToXml(XmlSerializationVisitor $visitor, FormError $formError, array $type)
94996 {
94997 if (null === $visitor->document) {
94998 $visitor->document = $visitor->createDocument(null, null, true);
94999 }
95000
95001 return $visitor->document->createCDATASection($this->getErrorMessage($formError));
95002 }
95003
95004 public function serializeFormErrorToJson(JsonSerializationVisitor $visitor, FormError $formError, array $type)
95005 {
95006 return $this->getErrorMessage($formError);
95007 }
95008
95009 public function serializeFormErrorToYml(YamlSerializationVisitor $visitor, FormError $formError, array $type)
95010 {
95011 return $this->getErrorMessage($formError);
95012 }
95013
95014 private function getErrorMessage(FormError $error)
95015 {
95016 if (null !== $error->getMessagePluralization()) {
95017 return $this->translator->transChoice($error->getMessageTemplate(), $error->getMessagePluralization(), $error->getMessageParameters(), 'validators');
95018 }
95019
95020 return $this->translator->trans($error->getMessageTemplate(), $error->getMessageParameters(), 'validators');
95021 }
95022
95023 private function convertFormToArray(GenericSerializationVisitor $visitor, Form $data)
95024 {
95025 $isRoot = null === $visitor->getRoot();
95026
95027 $form = $errors = array();
95028 foreach ($data->getErrors() as $error) {
95029 $errors[] = $this->getErrorMessage($error);
95030 }
95031
95032 if ($errors) {
95033 $form['errors'] = $errors;
95034 }
95035
95036 $children = array();
95037 foreach ($data->all() as $child) {
95038 $children[$child->getName()] = $this->convertFormToArray($visitor, $child);
95039 }
95040
95041 if ($children) {
95042 $form['children'] = $children;
95043 }
95044
95045 if ($isRoot) {
95046 $visitor->setRoot($form);
95047 }
95048
95049 return $form;
95050 }
95051 }
95052 <?php
95053
95054 /*
95055 * Copyright 2013 Johannes M. Schmitt <schmittjoh@gmail.com>
95056 *
95057 * Licensed under the Apache License, Version 2.0 (the "License");
95058 * you may not use this file except in compliance with the License.
95059 * You may obtain a copy of the License at
95060 *
95061 * http://www.apache.org/licenses/LICENSE-2.0
95062 *
95063 * Unless required by applicable law or agreed to in writing, software
95064 * distributed under the License is distributed on an "AS IS" BASIS,
95065 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
95066 * See the License for the specific language governing permissions and
95067 * limitations under the License.
95068 */
95069
95070 namespace JMS\Serializer\Handler;
95071
95072 use JMS\Serializer\GraphNavigator;
95073 use JMS\Serializer\Exception\RuntimeException;
95074 use JMS\Serializer\Exception\LogicException;
95075
95076 class HandlerRegistry implements HandlerRegistryInterface
95077 {
95078 protected $handlers;
95079
95080 public static function getDefaultMethod($direction, $type, $format)
95081 {
95082 if (false !== $pos = strrpos($type, '\\')) {
95083 $type = substr($type, $pos + 1);
95084 }
95085
95086 switch ($direction) {
95087 case GraphNavigator::DIRECTION_DESERIALIZATION:
95088 return 'deserialize'.$type.'From'.$format;
95089
95090 case GraphNavigator::DIRECTION_SERIALIZATION:
95091 return 'serialize'.$type.'To'.$format;
95092
95093 default:
95094 throw new LogicException(sprintf('The direction %s does not exist; see GraphNavigator::DIRECTION_??? constants.', json_encode($direction)));
95095 }
95096 }
95097
95098 public function __construct(array $handlers = array())
95099 {
95100 $this->handlers = $handlers;
95101 }
95102
95103 public function registerSubscribingHandler(SubscribingHandlerInterface $handler)
95104 {
95105 foreach ($handler->getSubscribingMethods() as $methodData) {
95106 if ( ! isset($methodData['type'], $methodData['format'])) {
95107 throw new RuntimeException(sprintf('For each subscribing method a "type" and "format" attribute must be given, but only got "%s" for %s.', implode('" and "', array_keys($methodData)), get_class($handler)));
95108 }
95109
95110 $directions = array(GraphNavigator::DIRECTION_DESERIALIZATION, GraphNavigator::DIRECTION_SERIALIZATION);
95111 if (isset($methodData['direction'])) {
95112 $directions = array($methodData['direction']);
95113 }
95114
95115 foreach ($directions as $direction) {
95116 $method = isset($methodData['method']) ? $methodData['method'] : self::getDefaultMethod($direction, $methodData['type'], $methodData['format']);
95117 $this->registerHandler($direction, $methodData['type'], $methodData['format'], array($handler, $method));
95118 }
95119 }
95120 }
95121
95122 public function registerHandler($direction, $typeName, $format, $handler)
95123 {
95124 if (is_string($direction)) {
95125 $direction = GraphNavigator::parseDirection($direction);
95126 }
95127
95128 $this->handlers[$direction][$typeName][$format] = $handler;
95129 }
95130
95131 public function getHandler($direction, $typeName, $format)
95132 {
95133 if ( ! isset($this->handlers[$direction][$typeName][$format])) {
95134 return null;
95135 }
95136
95137 return $this->handlers[$direction][$typeName][$format];
95138 }
95139 }
95140 <?php
95141
95142 /*
95143 * Copyright 2013 Johannes M. Schmitt <schmittjoh@gmail.com>
95144 *
95145 * Licensed under the Apache License, Version 2.0 (the "License");
95146 * you may not use this file except in compliance with the License.
95147 * You may obtain a copy of the License at
95148 *
95149 * http://www.apache.org/licenses/LICENSE-2.0
95150 *
95151 * Unless required by applicable law or agreed to in writing, software
95152 * distributed under the License is distributed on an "AS IS" BASIS,
95153 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
95154 * See the License for the specific language governing permissions and
95155 * limitations under the License.
95156 */
95157
95158 namespace JMS\Serializer\Handler;
95159
95160 interface SubscribingHandlerInterface
95161 {
95162 /**
95163 * Return format:
95164 *
95165 * array(
95166 * array(
95167 * 'direction' => GraphNavigator::DIRECTION_SERIALIZATION,
95168 * 'format' => 'json',
95169 * 'type' => 'DateTime',
95170 * 'method' => 'serializeDateTimeToJson',
95171 * ),
95172 * )
95173 *
95174 * The direction and method keys can be omitted.
95175 *
95176 * @return array
95177 */
95178 public static function getSubscribingMethods();
95179 }
95180 <?php
95181
95182 /*
95183 * Copyright 2013 Johannes M. Schmitt <schmittjoh@gmail.com>
95184 *
95185 * Licensed under the Apache License, Version 2.0 (the "License");
95186 * you may not use this file except in compliance with the License.
95187 * You may obtain a copy of the License at
95188 *
95189 * http://www.apache.org/licenses/LICENSE-2.0
95190 *
95191 * Unless required by applicable law or agreed to in writing, software
95192 * distributed under the License is distributed on an "AS IS" BASIS,
95193 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
95194 * See the License for the specific language governing permissions and
95195 * limitations under the License.
95196 */
95197
95198 namespace JMS\Serializer\Handler;
95199
95200 use Doctrine\Common\Collections\ArrayCollection;
95201 use JMS\Serializer\Context;
95202 use JMS\Serializer\GraphNavigator;
95203 use JMS\Serializer\VisitorInterface;
95204 use Doctrine\Common\Collections\Collection;
95205 use JMS\Serializer\Handler\SubscribingHandlerInterface;
95206
95207 class ArrayCollectionHandler implements SubscribingHandlerInterface
95208 {
95209 public static function getSubscribingMethods()
95210 {
95211 $methods = array();
95212 $formats = array('json', 'xml', 'yml');
95213 $collectionTypes = array('ArrayCollection', 'Doctrine\Common\Collections\ArrayCollection', 'Doctrine\ORM\PersistentCollection', 'Doctrine\ODM\MongoDB\PersistentCollection');
95214
95215 foreach ($collectionTypes as $type) {
95216 foreach ($formats as $format) {
95217 $methods[] = array(
95218 'direction' => GraphNavigator::DIRECTION_SERIALIZATION,
95219 'type' => $type,
95220 'format' => $format,
95221 'method' => 'serializeCollection',
95222 );
95223
95224 $methods[] = array(
95225 'direction' => GraphNavigator::DIRECTION_DESERIALIZATION,
95226 'type' => $type,
95227 'format' => $format,
95228 'method' => 'deserializeCollection',
95229 );
95230 }
95231 }
95232
95233 return $methods;
95234 }
95235
95236 public function serializeCollection(VisitorInterface $visitor, Collection $collection, array $type, Context $context)
95237 {
95238 // We change the base type, and pass through possible parameters.
95239 $type['name'] = 'array';
95240
95241 return $visitor->visitArray($collection->toArray(), $type, $context);
95242 }
95243
95244 public function deserializeCollection(VisitorInterface $visitor, $data, array $type, Context $context)
95245 {
95246 // See above.
95247 $type['name'] = 'array';
95248
95249 return new ArrayCollection($visitor->visitArray($data, $type, $context));
95250 }
95251 }
95252 <?php
95253
95254 /*
95255 * Copyright 2013 Johannes M. Schmitt <schmittjoh@gmail.com>
95256 *
95257 * Licensed under the Apache License, Version 2.0 (the "License");
95258 * you may not use this file except in compliance with the License.
95259 * You may obtain a copy of the License at
95260 *
95261 * http://www.apache.org/licenses/LICENSE-2.0
95262 *
95263 * Unless required by applicable law or agreed to in writing, software
95264 * distributed under the License is distributed on an "AS IS" BASIS,
95265 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
95266 * See the License for the specific language governing permissions and
95267 * limitations under the License.
95268 */
95269
95270 namespace JMS\Serializer\Handler;
95271
95272 use JMS\Serializer\Context;
95273 use JMS\Serializer\YamlSerializationVisitor;
95274 use JMS\Serializer\JsonSerializationVisitor;
95275 use JMS\Serializer\GraphNavigator;
95276 use Symfony\Component\Validator\ConstraintViolation;
95277 use Symfony\Component\Validator\ConstraintViolationList;
95278 use JMS\Serializer\XmlSerializationVisitor;
95279
95280 class ConstraintViolationHandler implements SubscribingHandlerInterface
95281 {
95282 public static function getSubscribingMethods()
95283 {
95284 $methods = array();
95285 $formats = array('xml', 'json', 'yml');
95286 $types = array('Symfony\Component\Validator\ConstraintViolationList' => 'serializeList', 'Symfony\Component\Validator\ConstraintViolation' => 'serializeViolation');
95287
95288 foreach ($types as $type => $method) {
95289 foreach ($formats as $format) {
95290 $methods[] = array(
95291 'direction' => GraphNavigator::DIRECTION_SERIALIZATION,
95292 'type' => $type,
95293 'format' => $format,
95294 'method' => $method.'To'.$format,
95295 );
95296 }
95297 }
95298
95299 return $methods;
95300 }
95301
95302 public function serializeListToXml(XmlSerializationVisitor $visitor, ConstraintViolationList $list, array $type)
95303 {
95304 if (null === $visitor->document) {
95305 $visitor->document = $visitor->createDocument();
95306 }
95307
95308 foreach ($list as $violation) {
95309 $this->serializeViolationToXml($visitor, $violation);
95310 }
95311 }
95312
95313 public function serializeListToJson(JsonSerializationVisitor $visitor, ConstraintViolationList $list, array $type, Context $context)
95314 {
95315 return $visitor->visitArray(iterator_to_array($list), $type, $context);
95316 }
95317
95318 public function serializeListToYml(YamlSerializationVisitor $visitor, ConstraintViolationList $list, array $type, Context $context)
95319 {
95320 return $visitor->visitArray(iterator_to_array($list), $type, $context);
95321 }
95322
95323 public function serializeViolationToXml(XmlSerializationVisitor $visitor, ConstraintViolation $violation, array $type = null)
95324 {
95325 if (null === $visitor->document) {
95326 $visitor->document = $visitor->createDocument(null, null, false);
95327 $visitor->document->appendChild($violationNode = $visitor->document->createElement('violation'));
95328 $visitor->setCurrentNode($violationNode);
95329 } else {
95330 $visitor->getCurrentNode()->appendChild(
95331 $violationNode = $visitor->document->createElement('violation')
95332 );
95333 }
95334
95335 $violationNode->setAttribute('property_path', $violation->getPropertyPath());
95336 $violationNode->appendChild($messageNode = $visitor->document->createElement('message'));
95337
95338 $messageNode->appendChild($visitor->document->createCDATASection($violation->getMessage()));
95339 }
95340
95341 public function serializeViolationToJson(JsonSerializationVisitor $visitor, ConstraintViolation $violation, array $type = null)
95342 {
95343 $data = array(
95344 'property_path' => $violation->getPropertyPath(),
95345 'message' => $violation->getMessage()
95346 );
95347
95348 if (null === $visitor->getRoot()) {
95349 $visitor->setRoot($data);
95350 }
95351
95352 return $data;
95353 }
95354
95355 public function serializeViolationToYml(YamlSerializationVisitor $visitor, ConstraintViolation $violation, array $type = null)
95356 {
95357 return array(
95358 'property_path' => $violation->getPropertyPath(),
95359 'message' => $violation->getMessage(),
95360 );
95361 }
95362 }
95363 <?php
95364
95365 /*
95366 * Copyright 2013 Johannes M. Schmitt <schmittjoh@gmail.com>
95367 *
95368 * Licensed under the Apache License, Version 2.0 (the "License");
95369 * you may not use this file except in compliance with the License.
95370 * You may obtain a copy of the License at
95371 *
95372 * http://www.apache.org/licenses/LICENSE-2.0
95373 *
95374 * Unless required by applicable law or agreed to in writing, software
95375 * distributed under the License is distributed on an "AS IS" BASIS,
95376 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
95377 * See the License for the specific language governing permissions and
95378 * limitations under the License.
95379 */
95380
95381 namespace JMS\Serializer\Handler;
95382
95383 use JMS\Serializer\Context;
95384 use JMS\Serializer\JsonDeserializationVisitor;
95385 use JMS\Serializer\XmlDeserializationVisitor;
95386 use JMS\Serializer\Exception\RuntimeException;
95387 use JMS\Serializer\VisitorInterface;
95388 use JMS\Serializer\GraphNavigator;
95389
95390 class DateHandler implements SubscribingHandlerInterface
95391 {
95392 private $defaultFormat;
95393 private $defaultTimezone;
95394
95395 public static function getSubscribingMethods()
95396 {
95397 $methods = array();
95398 $types = array('DateTime', 'DateInterval');
95399
95400 foreach (array('json', 'xml', 'yml') as $format) {
95401 $methods[] = array(
95402 'type' => 'DateTime',
95403 'direction' => GraphNavigator::DIRECTION_DESERIALIZATION,
95404 'format' => $format,
95405 );
95406
95407 foreach ($types as $type) {
95408 $methods[] = array(
95409 'type' => $type,
95410 'format' => $format,
95411 'direction' => GraphNavigator::DIRECTION_SERIALIZATION,
95412 'method' => 'serialize'.$type,
95413 );
95414 }
95415 }
95416
95417 return $methods;
95418 }
95419
95420 public function __construct($defaultFormat = \DateTime::ISO8601, $defaultTimezone = 'UTC')
95421 {
95422 $this->defaultFormat = $defaultFormat;
95423 $this->defaultTimezone = new \DateTimeZone($defaultTimezone);
95424 }
95425
95426 public function serializeDateTime(VisitorInterface $visitor, \DateTime $date, array $type, Context $context)
95427 {
95428 return $visitor->visitString($date->format($this->getFormat($type)), $type, $context);
95429 }
95430
95431 public function serializeDateInterval(VisitorInterface $visitor, \DateInterval $date, array $type, Context $context)
95432 {
95433 $iso8601DateIntervalString = $this->format($date);
95434
95435 return $visitor->visitString($iso8601DateIntervalString, $type, $context);
95436 }
95437
95438 public function deserializeDateTimeFromXml(XmlDeserializationVisitor $visitor, $data, array $type)
95439 {
95440 $attributes = $data->attributes();
95441 if ((isset($attributes['nil'][0]) && (string) $attributes['nil'][0] === 'true') ||
95442 (isset($attributes['xsi:nil'][0]) && (string) $attributes['xsi:nil'][0] === 'true')) {
95443 return null;
95444 }
95445
95446 return $this->parseDateTime($data, $type);
95447 }
95448
95449 public function deserializeDateTimeFromJson(JsonDeserializationVisitor $visitor, $data, array $type)
95450 {
95451 if (null === $data) {
95452 return null;
95453 }
95454
95455 return $this->parseDateTime($data, $type);
95456 }
95457
95458 private function parseDateTime($data, array $type)
95459 {
95460 $timezone = isset($type['params'][1]) ? new \DateTimeZone($type['params'][1]) : $this->defaultTimezone;
95461 $format = $this->getFormat($type);
95462 $datetime = \DateTime::createFromFormat($format, (string) $data, $timezone);
95463 if (false === $datetime) {
95464 throw new RuntimeException(sprintf('Invalid datetime "%s", expected format %s.', $data, $format));
95465 }
95466
95467 return $datetime;
95468 }
95469
95470 /**
95471 * @return string
95472 * @param array $type
95473 */
95474 private function getFormat(array $type)
95475 {
95476 return isset($type['params'][0]) ? $type['params'][0] : $this->defaultFormat;
95477 }
95478
95479 /**
95480 * @param \DateInterval $dateInterval
95481 * @return string
95482 */
95483 public function format(\DateInterval $dateInterval)
95484 {
95485 $format = 'P';
95486
95487 if (0 < $dateInterval->y) {
95488 $format .= $dateInterval->y.'Y';
95489 }
95490
95491 if (0 < $dateInterval->m) {
95492 $format .= $dateInterval->m.'M';
95493 }
95494
95495 if (0 < $dateInterval->d) {
95496 $format .= $dateInterval->d.'D';
95497 }
95498
95499 if (0 < $dateInterval->h || 0 < $dateInterval->i || 0 < $dateInterval->s) {
95500 $format .= 'T';
95501 }
95502
95503 if (0 < $dateInterval->h) {
95504 $format .= $dateInterval->h.'H';
95505 }
95506
95507 if (0 < $dateInterval->i) {
95508 $format .= $dateInterval->i.'M';
95509 }
95510
95511 if (0 < $dateInterval->s) {
95512 $format .= $dateInterval->s.'S';
95513 }
95514
95515 return $format;
95516 }
95517 }
95518 <?php
95519
95520 /*
95521 * Copyright 2013 Johannes M. Schmitt <schmittjoh@gmail.com>
95522 *
95523 * Licensed under the Apache License, Version 2.0 (the "License");
95524 * you may not use this file except in compliance with the License.
95525 * You may obtain a copy of the License at
95526 *
95527 * http://www.apache.org/licenses/LICENSE-2.0
95528 *
95529 * Unless required by applicable law or agreed to in writing, software
95530 * distributed under the License is distributed on an "AS IS" BASIS,
95531 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
95532 * See the License for the specific language governing permissions and
95533 * limitations under the License.
95534 */
95535
95536 namespace JMS\Serializer\Handler;
95537
95538 use JMS\Serializer\Context;
95539 use JMS\Serializer\GraphNavigator;
95540 use JMS\Serializer\VisitorInterface;
95541 use PhpCollection\Map;
95542 use PhpCollection\Sequence;
95543
95544 class PhpCollectionHandler implements SubscribingHandlerInterface
95545 {
95546 public static function getSubscribingMethods()
95547 {
95548 $methods = array();
95549 $formats = array('json', 'xml', 'yml');
95550 $collectionTypes = array(
95551 'PhpCollection\Sequence' => 'Sequence',
95552 'PhpCollection\Map' => 'Map',
95553 );
95554
95555 foreach ($collectionTypes as $type => $shortName) {
95556 foreach ($formats as $format) {
95557 $methods[] = array(
95558 'direction' => GraphNavigator::DIRECTION_SERIALIZATION,
95559 'type' => $type,
95560 'format' => $format,
95561 'method' => 'serialize'.$shortName,
95562 );
95563
95564 $methods[] = array(
95565 'direction' => GraphNavigator::DIRECTION_DESERIALIZATION,
95566 'type' => $type,
95567 'format' => $format,
95568 'method' => 'deserialize'.$shortName,
95569 );
95570 }
95571 }
95572
95573 return $methods;
95574 }
95575
95576 public function serializeMap(VisitorInterface $visitor, Map $map, array $type, Context $context)
95577 {
95578 $type['name'] = 'array';
95579
95580 return $visitor->visitArray(iterator_to_array($map), $type, $context);
95581 }
95582
95583 public function deserializeMap(VisitorInterface $visitor, $data, array $type, Context $context)
95584 {
95585 $type['name'] = 'array';
95586
95587 return new Map($visitor->visitArray($data, $type, $context));
95588 }
95589
95590 public function serializeSequence(VisitorInterface $visitor, Sequence $sequence, array $type, Context $context)
95591 {
95592 // We change the base type, and pass through possible parameters.
95593 $type['name'] = 'array';
95594
95595 return $visitor->visitArray($sequence->all(), $type, $context);
95596 }
95597
95598 public function deserializeSequence(VisitorInterface $visitor, $data, array $type, Context $context)
95599 {
95600 // See above.
95601 $type['name'] = 'array';
95602
95603 return new Sequence($visitor->visitArray($data, $type, $context));
95604 }
95605 }<?php
95606
95607 /*
95608 * Copyright 2013 Johannes M. Schmitt <schmittjoh@gmail.com>
95609 *
95610 * Licensed under the Apache License, Version 2.0 (the "License");
95611 * you may not use this file except in compliance with the License.
95612 * You may obtain a copy of the License at
95613 *
95614 * http://www.apache.org/licenses/LICENSE-2.0
95615 *
95616 * Unless required by applicable law or agreed to in writing, software
95617 * distributed under the License is distributed on an "AS IS" BASIS,
95618 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
95619 * See the License for the specific language governing permissions and
95620 * limitations under the License.
95621 */
95622
95623 namespace JMS\Serializer\Handler;
95624
95625 use Symfony\Component\DependencyInjection\ContainerInterface;
95626
95627 class LazyHandlerRegistry extends HandlerRegistry
95628 {
95629 private $container;
95630 private $initializedHandlers = array();
95631
95632 public function __construct(ContainerInterface $container, array $handlers = array())
95633 {
95634 parent::__construct($handlers);
95635 $this->container = $container;
95636 }
95637
95638 public function registerHandler($direction, $typeName, $format, $handler)
95639 {
95640 parent::registerHandler($direction, $typeName, $format, $handler);
95641 unset($this->initializedHandlers[$direction][$typeName][$format]);
95642 }
95643
95644 public function getHandler($direction, $typeName, $format)
95645 {
95646 if (isset($this->initializedHandlers[$direction][$typeName][$format])) {
95647 return $this->initializedHandlers[$direction][$typeName][$format];
95648 }
95649
95650 if ( ! isset($this->handlers[$direction][$typeName][$format])) {
95651 return null;
95652 }
95653
95654 $handler = $this->handlers[$direction][$typeName][$format];
95655 if (is_array($handler) && is_string($handler[0]) && $this->container->has($handler[0])) {
95656 $handler[0] = $this->container->get($handler[0]);
95657 }
95658
95659 return $this->initializedHandlers[$direction][$typeName][$format] = $handler;
95660 }
95661 }
95662 <?php
95663
95664 /*
95665 * Copyright 2013 Johannes M. Schmitt <schmittjoh@gmail.com>
95666 *
95667 * Licensed under the Apache License, Version 2.0 (the "License");
95668 * you may not use this file except in compliance with the License.
95669 * You may obtain a copy of the License at
95670 *
95671 * http://www.apache.org/licenses/LICENSE-2.0
95672 *
95673 * Unless required by applicable law or agreed to in writing, software
95674 * distributed under the License is distributed on an "AS IS" BASIS,
95675 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
95676 * See the License for the specific language governing permissions and
95677 * limitations under the License.
95678 */
95679
95680 namespace JMS\Serializer\Handler;
95681
95682 /**
95683 * Handler Registry Interface.
95684 *
95685 * @author Johannes M. Schmitt <schmittjoh@gmail.com>
95686 */
95687 interface HandlerRegistryInterface
95688 {
95689 /**
95690 * @param SubscribingHandlerInterface $handler
95691 *
95692 * @return void
95693 */
95694 public function registerSubscribingHandler(SubscribingHandlerInterface $handler);
95695
95696 /**
95697 * Registers a handler in the registry.
95698 *
95699 * @param integer $direction one of the GraphNavigator::DIRECTION_??? constants
95700 * @param string $typeName
95701 * @param string $format
95702 * @param callable $handler function(VisitorInterface, mixed $data, array $type): mixed
95703 *
95704 * @return void
95705 */
95706 public function registerHandler($direction, $typeName, $format, $handler);
95707
95708 /**
95709 * @param integer $direction one of the GraphNavigator::DIRECTION_??? constants
95710 * @param string $typeName
95711 * @param string $format
95712 *
95713 * @return callable|null
95714 */
95715 public function getHandler($direction, $typeName, $format);
95716 }
95717 <?php
95718
95719 /*
95720 * Copyright 2013 Johannes M. Schmitt <schmittjoh@gmail.com>
95721 *
95722 * Licensed under the Apache License, Version 2.0 (the "License");
95723 * you may not use this file except in compliance with the License.
95724 * You may obtain a copy of the License at
95725 *
95726 * http://www.apache.org/licenses/LICENSE-2.0
95727 *
95728 * Unless required by applicable law or agreed to in writing, software
95729 * distributed under the License is distributed on an "AS IS" BASIS,
95730 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
95731 * See the License for the specific language governing permissions and
95732 * limitations under the License.
95733 */
95734
95735 namespace JMS\Serializer;
95736
95737 use JMS\Serializer\Construction\ObjectConstructorInterface;
95738 use JMS\Serializer\Handler\HandlerRegistryInterface;
95739 use JMS\Serializer\EventDispatcher\EventDispatcherInterface;
95740 use JMS\Serializer\Exception\UnsupportedFormatException;
95741 use Metadata\MetadataFactoryInterface;
95742 use PhpCollection\MapInterface;
95743
95744 /**
95745 * Serializer Implementation.
95746 *
95747 * @author Johannes M. Schmitt <schmittjoh@gmail.com>
95748 */
95749 class Serializer implements SerializerInterface
95750 {
95751 private $factory;
95752 private $handlerRegistry;
95753 private $objectConstructor;
95754 private $dispatcher;
95755 private $typeParser;
95756
95757 /** @var \PhpCollection\MapInterface */
95758 private $serializationVisitors;
95759
95760 /** @var \PhpCollection\MapInterface */
95761 private $deserializationVisitors;
95762
95763 private $navigator;
95764
95765 /**
95766 * Constructor.
95767 *
95768 * @param \Metadata\MetadataFactoryInterface $factory
95769 * @param Handler\HandlerRegistryInterface $handlerRegistry
95770 * @param Construction\ObjectConstructorInterface $objectConstructor
95771 * @param \PhpCollection\MapInterface<VisitorInterface> $serializationVisitors
95772 * @param \PhpCollection\MapInterface<VisitorInterface> $deserializationVisitors
95773 * @param EventDispatcher\EventDispatcherInterface $dispatcher
95774 * @param TypeParser $typeParser
95775 */
95776 public function __construct(MetadataFactoryInterface $factory, HandlerRegistryInterface $handlerRegistry, ObjectConstructorInterface $objectConstructor, MapInterface $serializationVisitors, MapInterface $deserializationVisitors, EventDispatcherInterface $dispatcher = null, TypeParser $typeParser = null)
95777 {
95778 $this->factory = $factory;
95779 $this->handlerRegistry = $handlerRegistry;
95780 $this->objectConstructor = $objectConstructor;
95781 $this->dispatcher = $dispatcher;
95782 $this->typeParser = $typeParser ?: new TypeParser();
95783 $this->serializationVisitors = $serializationVisitors;
95784 $this->deserializationVisitors = $deserializationVisitors;
95785
95786 $this->navigator = new GraphNavigator($this->factory, $this->handlerRegistry, $this->objectConstructor, $this->dispatcher);
95787 }
95788
95789 public function serialize($data, $format, SerializationContext $context = null)
95790 {
95791 if ( ! $this->serializationVisitors->containsKey($format)) {
95792 throw new UnsupportedFormatException(sprintf('The format "%s" is not supported for serialization.', $format));
95793 }
95794
95795 if (null === $context) {
95796 $context = new SerializationContext();
95797 }
95798
95799 $context->initialize(
95800 $format,
95801 $visitor = $this->serializationVisitors->get($format)->get(),
95802 $this->navigator,
95803 $this->factory
95804 );
95805
95806 $visitor->setNavigator($this->navigator);
95807 $this->navigator->accept($visitor->prepare($data), null, $context);
95808
95809 return $visitor->getResult();
95810 }
95811
95812 public function deserialize($data, $type, $format, DeserializationContext $context = null)
95813 {
95814 if ( ! $this->deserializationVisitors->containsKey($format)) {
95815 throw new UnsupportedFormatException(sprintf('The format "%s" is not supported for deserialization.', $format));
95816 }
95817
95818 if (null === $context) {
95819 $context = new DeserializationContext();
95820 }
95821
95822 $context->initialize(
95823 $format,
95824 $visitor = $this->deserializationVisitors->get($format)->get(),
95825 $this->navigator,
95826 $this->factory
95827 );
95828
95829 $visitor->setNavigator($this->navigator);
95830 $navigatorResult = $this->navigator->accept($visitor->prepare($data), $this->typeParser->parse($type), $context);
95831
95832 // This is a special case if the root is handled by a callback on the object iself.
95833 if ((null === $visitorResult = $visitor->getResult()) && null !== $navigatorResult) {
95834 return $navigatorResult;
95835 }
95836
95837 return $visitorResult;
95838 }
95839
95840 /**
95841 * @return MetadataFactoryInterface
95842 */
95843 public function getMetadataFactory()
95844 {
95845 return $this->factory;
95846 }
95847 }
95848 <?php
95849
95850 /*
95851 * Copyright 2013 Johannes M. Schmitt <schmittjoh@gmail.com>
95852 *
95853 * Licensed under the Apache License, Version 2.0 (the "License");
95854 * you may not use this file except in compliance with the License.
95855 * You may obtain a copy of the License at
95856 *
95857 * http://www.apache.org/licenses/LICENSE-2.0
95858 *
95859 * Unless required by applicable law or agreed to in writing, software
95860 * distributed under the License is distributed on an "AS IS" BASIS,
95861 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
95862 * See the License for the specific language governing permissions and
95863 * limitations under the License.
95864 */
95865
95866 namespace JMS\Serializer;
95867
95868 use JMS\Serializer\Metadata\ClassMetadata;
95869
95870 class JsonSerializationVisitor extends GenericSerializationVisitor
95871 {
95872 private $options = 0;
95873
95874 public function getResult()
95875 {
95876 return json_encode($this->getRoot(), $this->options);
95877 }
95878
95879 public function getOptions()
95880 {
95881 return $this->options;
95882 }
95883
95884 public function setOptions($options)
95885 {
95886 $this->options = (integer) $options;
95887 }
95888
95889 public function visitArray($data, array $type, Context $context)
95890 {
95891 $result = parent::visitArray($data, $type, $context);
95892
95893 if (null !== $this->getRoot() && isset($type['params'][1]) && 0 === count($result)) {
95894 // ArrayObject is specially treated by the json_encode function and
95895 // serialized to { } while a mere array would be serialized to [].
95896 return new \ArrayObject();
95897 }
95898
95899 return $result;
95900 }
95901
95902 public function endVisitingObject(ClassMetadata $metadata, $data, array $type, Context $context)
95903 {
95904 $rs = parent::endVisitingObject($metadata, $data, $type, $context);
95905
95906 // Force JSON output to "{}" instead of "[]" if it contains either no properties or all properties are null.
95907 if (empty($rs)) {
95908 $rs = new \ArrayObject();
95909
95910 if (array() === $this->getRoot()) {
95911 $this->setRoot($rs);
95912 }
95913 }
95914
95915 return $rs;
95916 }
95917 }
95918 <?php
95919
95920 /*
95921 * Copyright 2013 Johannes M. Schmitt <schmittjoh@gmail.com>
95922 *
95923 * Licensed under the Apache License, Version 2.0 (the "License");
95924 * you may not use this file except in compliance with the License.
95925 * You may obtain a copy of the License at
95926 *
95927 * http://www.apache.org/licenses/LICENSE-2.0
95928 *
95929 * Unless required by applicable law or agreed to in writing, software
95930 * distributed under the License is distributed on an "AS IS" BASIS,
95931 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
95932 * See the License for the specific language governing permissions and
95933 * limitations under the License.
95934 */
95935
95936 namespace JMS\Serializer;
95937
95938 use JMS\Serializer\Metadata\ClassMetadata;
95939 use JMS\Serializer\Exception\InvalidArgumentException;
95940 use JMS\Serializer\Metadata\PropertyMetadata;
95941
95942 abstract class GenericSerializationVisitor extends AbstractVisitor
95943 {
95944 private $navigator;
95945 private $root;
95946 private $dataStack;
95947 private $data;
95948
95949 public function setNavigator(GraphNavigator $navigator)
95950 {
95951 $this->navigator = $navigator;
95952 $this->root = null;
95953 $this->dataStack = new \SplStack;
95954 }
95955
95956 /**
95957 * @return GraphNavigator
95958 */
95959 public function getNavigator()
95960 {
95961 return $this->navigator;
95962 }
95963
95964 public function visitNull($data, array $type, Context $context)
95965 {
95966 return null;
95967 }
95968
95969 public function visitString($data, array $type, Context $context)
95970 {
95971 if (null === $this->root) {
95972 $this->root = $data;
95973 }
95974
95975 return (string) $data;
95976 }
95977
95978 public function visitBoolean($data, array $type, Context $context)
95979 {
95980 if (null === $this->root) {
95981 $this->root = $data;
95982 }
95983
95984 return (boolean) $data;
95985 }
95986
95987 public function visitInteger($data, array $type, Context $context)
95988 {
95989 if (null === $this->root) {
95990 $this->root = $data;
95991 }
95992
95993 return (int) $data;
95994 }
95995
95996 public function visitDouble($data, array $type, Context $context)
95997 {
95998 if (null === $this->root) {
95999 $this->root = $data;
96000 }
96001
96002 return (float) $data;
96003 }
96004
96005 /**
96006 * @param array $data
96007 * @param array $type
96008 */
96009 public function visitArray($data, array $type, Context $context)
96010 {
96011 if (null === $this->root) {
96012 $this->root = array();
96013 $rs = &$this->root;
96014 } else {
96015 $rs = array();
96016 }
96017
96018 foreach ($data as $k => $v) {
96019 $v = $this->navigator->accept($v, isset($type['params'][1]) ? $type['params'][1] : null, $context);
96020
96021 if (null === $v && (!is_string($k) || !$context->shouldSerializeNull())) {
96022 continue;
96023 }
96024
96025 $rs[$k] = $v;
96026 }
96027
96028 return $rs;
96029 }
96030
96031 public function startVisitingObject(ClassMetadata $metadata, $data, array $type, Context $context)
96032 {
96033 if (null === $this->root) {
96034 $this->root = new \stdClass;
96035 }
96036
96037 $this->dataStack->push($this->data);
96038 $this->data = array();
96039 }
96040
96041 public function endVisitingObject(ClassMetadata $metadata, $data, array $type, Context $context)
96042 {
96043 $rs = $this->data;
96044 $this->data = $this->dataStack->pop();
96045
96046 if ($this->root instanceof \stdClass && 0 === $this->dataStack->count()) {
96047 $this->root = $rs;
96048 }
96049
96050 return $rs;
96051 }
96052
96053 public function visitProperty(PropertyMetadata $metadata, $data, Context $context)
96054 {
96055 $v = $metadata->getValue($data);
96056
96057 $v = $this->navigator->accept($v, $metadata->type, $context);
96058 if (null === $v && !$context->shouldSerializeNull()) {
96059 return;
96060 }
96061
96062 $k = $this->namingStrategy->translateName($metadata);
96063
96064 if ($metadata->inline && is_array($v)) {
96065 $this->data = array_merge($this->data, $v);
96066 } else {
96067 $this->data[$k] = $v;
96068 }
96069 }
96070
96071 /**
96072 * Allows you to add additional data to the current object/root element.
96073 *
96074 * @param string $key
96075 * @param scalar|array $value This value must either be a regular scalar, or an array.
96076 * It must not contain any objects anymore.
96077 */
96078 public function addData($key, $value)
96079 {
96080 if (isset($this->data[$key])) {
96081 throw new InvalidArgumentException(sprintf('There is already data for "%s".', $key));
96082 }
96083
96084 $this->data[$key] = $value;
96085 }
96086
96087 public function getRoot()
96088 {
96089 return $this->root;
96090 }
96091
96092 /**
96093 * @param array|\ArrayObject $data the passed data must be understood by whatever encoding function is applied later.
96094 */
96095 public function setRoot($data)
96096 {
96097 $this->root = $data;
96098 }
96099 }
96100 <?php
96101
96102 /*
96103 * Copyright 2013 Johannes M. Schmitt <schmittjoh@gmail.com>
96104 *
96105 * Licensed under the Apache License, Version 2.0 (the "License");
96106 * you may not use this file except in compliance with the License.
96107 * You may obtain a copy of the License at
96108 *
96109 * http://www.apache.org/licenses/LICENSE-2.0
96110 *
96111 * Unless required by applicable law or agreed to in writing, software
96112 * distributed under the License is distributed on an "AS IS" BASIS,
96113 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
96114 * See the License for the specific language governing permissions and
96115 * limitations under the License.
96116 */
96117
96118 namespace JMS\Serializer;
96119
96120 use JMS\Serializer\Exception\RuntimeException;
96121 use JMS\Serializer\Metadata\PropertyMetadata;
96122 use JMS\Serializer\Metadata\ClassMetadata;
96123
96124 /**
96125 * Generic Deserialization Visitor.
96126 *
96127 * @author Johannes M. Schmitt <schmittjoh@gmail.com>
96128 */
96129 abstract class GenericDeserializationVisitor extends AbstractVisitor
96130 {
96131 private $navigator;
96132 private $result;
96133 private $objectStack;
96134 private $currentObject;
96135
96136 public function setNavigator(GraphNavigator $navigator)
96137 {
96138 $this->navigator = $navigator;
96139 $this->result = null;
96140 $this->objectStack = new \SplStack;
96141 }
96142
96143 public function getNavigator()
96144 {
96145 return $this->navigator;
96146 }
96147
96148 public function prepare($data)
96149 {
96150 return $this->decode($data);
96151 }
96152
96153 public function visitNull($data, array $type, Context $context)
96154 {
96155 return null;
96156 }
96157
96158 public function visitString($data, array $type, Context $context)
96159 {
96160 $data = (string) $data;
96161
96162 if (null === $this->result) {
96163 $this->result = $data;
96164 }
96165
96166 return $data;
96167 }
96168
96169 public function visitBoolean($data, array $type, Context $context)
96170 {
96171 $data = (Boolean) $data;
96172
96173 if (null === $this->result) {
96174 $this->result = $data;
96175 }
96176
96177 return $data;
96178 }
96179
96180 public function visitInteger($data, array $type, Context $context)
96181 {
96182 $data = (integer) $data;
96183
96184 if (null === $this->result) {
96185 $this->result = $data;
96186 }
96187
96188 return $data;
96189 }
96190
96191 public function visitDouble($data, array $type, Context $context)
96192 {
96193 $data = (double) $data;
96194
96195 if (null === $this->result) {
96196 $this->result = $data;
96197 }
96198
96199 return $data;
96200 }
96201
96202 public function visitArray($data, array $type, Context $context)
96203 {
96204 if ( ! is_array($data)) {
96205 throw new RuntimeException(sprintf('Expected array, but got %s: %s', gettype($data), json_encode($data)));
96206 }
96207
96208 // If no further parameters were given, keys/values are just passed as is.
96209 if ( ! $type['params']) {
96210 if (null === $this->result) {
96211 $this->result = $data;
96212 }
96213
96214 return $data;
96215 }
96216
96217 switch (count($type['params'])) {
96218 case 1: // Array is a list.
96219 $listType = $type['params'][0];
96220
96221 $result = array();
96222 if (null === $this->result) {
96223 $this->result = &$result;
96224 }
96225
96226 foreach ($data as $v) {
96227 $result[] = $this->navigator->accept($v, $listType, $context);
96228 }
96229
96230 return $result;
96231
96232 case 2: // Array is a map.
96233 list($keyType, $entryType) = $type['params'];
96234
96235 $result = array();
96236 if (null === $this->result) {
96237 $this->result = &$result;
96238 }
96239
96240 foreach ($data as $k => $v) {
96241 $result[$this->navigator->accept($k, $keyType, $context)] = $this->navigator->accept($v, $entryType, $context);
96242 }
96243
96244 return $result;
96245
96246 default:
96247 throw new RuntimeException(sprintf('Array type cannot have more than 2 parameters, but got %s.', json_encode($type['params'])));
96248 }
96249 }
96250
96251 public function startVisitingObject(ClassMetadata $metadata, $object, array $type, Context $context)
96252 {
96253 $this->setCurrentObject($object);
96254
96255 if (null === $this->result) {
96256 $this->result = $this->currentObject;
96257 }
96258 }
96259
96260 public function visitProperty(PropertyMetadata $metadata, $data, Context $context)
96261 {
96262 $name = $this->namingStrategy->translateName($metadata);
96263
96264 if (null === $data || ! array_key_exists($name, $data)) {
96265 return;
96266 }
96267
96268 if ( ! $metadata->type) {
96269 throw new RuntimeException(sprintf('You must define a type for %s::$%s.', $metadata->reflection->class, $metadata->name));
96270 }
96271
96272 $v = $data[$name] !== null ? $this->navigator->accept($data[$name], $metadata->type, $context) : null;
96273
96274 if (null === $metadata->setter) {
96275 $metadata->reflection->setValue($this->currentObject, $v);
96276
96277 return;
96278 }
96279
96280 $this->currentObject->{$metadata->setter}($v);
96281 }
96282
96283 public function endVisitingObject(ClassMetadata $metadata, $data, array $type, Context $context)
96284 {
96285 $obj = $this->currentObject;
96286 $this->revertCurrentObject();
96287
96288 return $obj;
96289 }
96290
96291 public function getResult()
96292 {
96293 return $this->result;
96294 }
96295
96296 public function setCurrentObject($object)
96297 {
96298 $this->objectStack->push($this->currentObject);
96299 $this->currentObject = $object;
96300 }
96301
96302 public function getCurrentObject()
96303 {
96304 return $this->currentObject;
96305 }
96306
96307 public function revertCurrentObject()
96308 {
96309 return $this->currentObject = $this->objectStack->pop();
96310 }
96311
96312 abstract protected function decode($str);
96313 }
96314 <?php
96315
96316 /*
96317 * Copyright 2013 Johannes M. Schmitt <schmittjoh@gmail.com>
96318 *
96319 * Licensed under the Apache License, Version 2.0 (the "License");
96320 * you may not use this file except in compliance with the License.
96321 * You may obtain a copy of the License at
96322 *
96323 * http://www.apache.org/licenses/LICENSE-2.0
96324 *
96325 * Unless required by applicable law or agreed to in writing, software
96326 * distributed under the License is distributed on an "AS IS" BASIS,
96327 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
96328 * See the License for the specific language governing permissions and
96329 * limitations under the License.
96330 */
96331
96332 namespace JMS\Serializer;
96333
96334 use JMS\Serializer\EventDispatcher\ObjectEvent;
96335 use JMS\Serializer\EventDispatcher\PreDeserializeEvent;
96336 use JMS\Serializer\EventDispatcher\PreSerializeEvent;
96337 use JMS\Serializer\Exception\RuntimeException;
96338 use JMS\Serializer\Construction\ObjectConstructorInterface;
96339 use JMS\Serializer\Handler\HandlerRegistryInterface;
96340 use JMS\Serializer\EventDispatcher\EventDispatcherInterface;
96341 use JMS\Serializer\Metadata\ClassMetadata;
96342 use Metadata\MetadataFactoryInterface;
96343 use JMS\Serializer\Exception\InvalidArgumentException;
96344
96345 /**
96346 * Handles traversal along the object graph.
96347 *
96348 * This class handles traversal along the graph, and calls different methods
96349 * on visitors, or custom handlers to process its nodes.
96350 *
96351 * @author Johannes M. Schmitt <schmittjoh@gmail.com>
96352 */
96353 final class GraphNavigator
96354 {
96355 const DIRECTION_SERIALIZATION = 1;
96356 const DIRECTION_DESERIALIZATION = 2;
96357
96358 private $dispatcher;
96359 private $metadataFactory;
96360 private $handlerRegistry;
96361 private $objectConstructor;
96362
96363 /**
96364 * Parses a direction string to one of the direction constants.
96365 *
96366 * @param string $dirStr
96367 *
96368 * @return integer
96369 */
96370 public static function parseDirection($dirStr)
96371 {
96372 switch (strtolower($dirStr)) {
96373 case 'serialization':
96374 return self::DIRECTION_SERIALIZATION;
96375
96376 case 'deserialization':
96377 return self::DIRECTION_DESERIALIZATION;
96378
96379 default:
96380 throw new InvalidArgumentException(sprintf('The direction "%s" does not exist.', $dirStr));
96381 }
96382 }
96383
96384 public function __construct(MetadataFactoryInterface $metadataFactory, HandlerRegistryInterface $handlerRegistry, ObjectConstructorInterface $objectConstructor, EventDispatcherInterface $dispatcher = null)
96385 {
96386 $this->dispatcher = $dispatcher;
96387 $this->metadataFactory = $metadataFactory;
96388 $this->handlerRegistry = $handlerRegistry;
96389 $this->objectConstructor = $objectConstructor;
96390 }
96391
96392 /**
96393 * Called for each node of the graph that is being traversed.
96394 *
96395 * @param mixed $data the data depends on the direction, and type of visitor
96396 * @param null|array $type array has the format ["name" => string, "params" => array]
96397 * @param VisitorInterface $visitor
96398 *
96399 * @return mixed the return value depends on the direction, and type of visitor
96400 */
96401 public function accept($data, array $type = null, Context $context)
96402 {
96403 $visitor = $context->getVisitor();
96404
96405 // If the type was not given, we infer the most specific type from the
96406 // input data in serialization mode.
96407 if (null === $type) {
96408 if ($context instanceof DeserializationContext) {
96409 throw new RuntimeException('The type must be given for all properties when deserializing.');
96410 }
96411
96412 $typeName = gettype($data);
96413 if ('object' === $typeName) {
96414 $typeName = get_class($data);
96415 }
96416
96417 $type = array('name' => $typeName, 'params' => array());
96418 }
96419 // If the data is null, we have to force the type to null regardless of the input in order to
96420 // guarantee correct handling of null values, and not have any internal auto-casting behavior.
96421 else if ($context instanceof SerializationContext && null === $data) {
96422 $type = array('name' => 'NULL', 'params' => array());
96423 }
96424
96425 switch ($type['name']) {
96426 case 'NULL':
96427 return $visitor->visitNull($data, $type, $context);
96428
96429 case 'string':
96430 return $visitor->visitString($data, $type, $context);
96431
96432 case 'integer':
96433 return $visitor->visitInteger($data, $type, $context);
96434
96435 case 'boolean':
96436 return $visitor->visitBoolean($data, $type, $context);
96437
96438 case 'double':
96439 case 'float':
96440 return $visitor->visitDouble($data, $type, $context);
96441
96442 case 'array':
96443 return $visitor->visitArray($data, $type, $context);
96444
96445 case 'resource':
96446 $msg = 'Resources are not supported in serialized data.';
96447 if ($context instanceof SerializationContext && null !== $path = $context->getPath()) {
96448 $msg .= ' Path: '.$path;
96449 }
96450
96451 throw new RuntimeException($msg);
96452
96453 default:
96454 // TODO: The rest of this method needs some refactoring.
96455 if ($context instanceof SerializationContext) {
96456 if (null !== $data) {
96457 if ($context->isVisiting($data)) {
96458 return null;
96459 }
96460 $context->startVisiting($data);
96461 }
96462 } elseif ($context instanceof DeserializationContext) {
96463 $context->increaseDepth();
96464 }
96465
96466 // Trigger pre-serialization callbacks, and listeners if they exist.
96467 // Dispatch pre-serialization event before handling data to have ability change type in listener
96468 if ($context instanceof SerializationContext) {
96469 if (null !== $this->dispatcher && $this->dispatcher->hasListeners('serializer.pre_serialize', $type['name'], $context->getFormat())) {
96470 $this->dispatcher->dispatch('serializer.pre_serialize', $type['name'], $context->getFormat(), $event = new PreSerializeEvent($context, $data, $type));
96471 $type = $event->getType();
96472 }
96473 } elseif ($context instanceof DeserializationContext) {
96474 if (null !== $this->dispatcher && $this->dispatcher->hasListeners('serializer.pre_deserialize', $type['name'], $context->getFormat())) {
96475 $this->dispatcher->dispatch('serializer.pre_deserialize', $type['name'], $context->getFormat(), $event = new PreDeserializeEvent($context, $data, $type));
96476 $type = $event->getType();
96477 $data = $event->getData();
96478 }
96479 }
96480
96481 // First, try whether a custom handler exists for the given type. This is done
96482 // before loading metadata because the type name might not be a class, but
96483 // could also simply be an artifical type.
96484 if (null !== $handler = $this->handlerRegistry->getHandler($context->getDirection(), $type['name'], $context->getFormat())) {
96485 $rs = call_user_func($handler, $visitor, $data, $type, $context);
96486 $this->leaveScope($context, $data);
96487
96488 return $rs;
96489 }
96490
96491 $exclusionStrategy = $context->getExclusionStrategy();
96492
96493 /** @var $metadata ClassMetadata */
96494 $metadata = $this->metadataFactory->getMetadataForClass($type['name']);
96495
96496 if ($context instanceof DeserializationContext && ! empty($metadata->discriminatorMap) && $type['name'] === $metadata->discriminatorBaseClass) {
96497 $metadata = $this->resolveMetadata($context, $data, $metadata);
96498 }
96499
96500 if (null !== $exclusionStrategy && $exclusionStrategy->shouldSkipClass($metadata, $context)) {
96501 $this->leaveScope($context, $data);
96502
96503 return null;
96504 }
96505
96506 $context->pushClassMetadata($metadata);
96507
96508 if ($context instanceof SerializationContext) {
96509 foreach ($metadata->preSerializeMethods as $method) {
96510 $method->invoke($data);
96511 }
96512 }
96513
96514 $object = $data;
96515 if ($context instanceof DeserializationContext) {
96516 $object = $this->objectConstructor->construct($visitor, $metadata, $data, $type);
96517 }
96518
96519 if (isset($metadata->handlerCallbacks[$context->getDirection()][$context->getFormat()])) {
96520 $rs = $object->{$metadata->handlerCallbacks[$context->getDirection()][$context->getFormat()]}(
96521 $visitor,
96522 $context instanceof SerializationContext ? null : $data,
96523 $context
96524 );
96525 $this->afterVisitingObject($metadata, $object, $type, $context);
96526
96527 return $context instanceof SerializationContext ? $rs : $object;
96528 }
96529
96530 $visitor->startVisitingObject($metadata, $object, $type, $context);
96531 foreach ($metadata->propertyMetadata as $propertyMetadata) {
96532 if (null !== $exclusionStrategy && $exclusionStrategy->shouldSkipProperty($propertyMetadata, $context)) {
96533 continue;
96534 }
96535
96536 if ($context instanceof DeserializationContext && $propertyMetadata->readOnly) {
96537 continue;
96538 }
96539
96540 $context->pushPropertyMetadata($propertyMetadata);
96541 $visitor->visitProperty($propertyMetadata, $data, $context);
96542 $context->popPropertyMetadata();
96543 }
96544
96545 if ($context instanceof SerializationContext) {
96546 $this->afterVisitingObject($metadata, $data, $type, $context);
96547
96548 return $visitor->endVisitingObject($metadata, $data, $type, $context);
96549 }
96550
96551 $rs = $visitor->endVisitingObject($metadata, $data, $type, $context);
96552 $this->afterVisitingObject($metadata, $rs, $type, $context);
96553
96554 return $rs;
96555 }
96556 }
96557
96558 private function resolveMetadata(DeserializationContext $context, $data, ClassMetadata $metadata)
96559 {
96560 switch (true) {
96561 case is_array($data) && isset($data[$metadata->discriminatorFieldName]):
96562 $typeValue = (string) $data[$metadata->discriminatorFieldName];
96563 break;
96564
96565 case is_object($data) && isset($data->{$metadata->discriminatorFieldName}):
96566 $typeValue = (string) $data->{$metadata->discriminatorFieldName};
96567 break;
96568
96569 default:
96570 throw new \LogicException(sprintf(
96571 'The discriminator field name "%s" for base-class "%s" was not found in input data.',
96572 $metadata->discriminatorFieldName,
96573 $metadata->name
96574 ));
96575 }
96576
96577 if ( ! isset($metadata->discriminatorMap[$typeValue])) {
96578 throw new \LogicException(sprintf(
96579 'The type value "%s" does not exist in the discriminator map of class "%s". Available types: %s',
96580 $typeValue,
96581 $metadata->name,
96582 implode(', ', array_keys($metadata->discriminatorMap))
96583 ));
96584 }
96585
96586 return $this->metadataFactory->getMetadataForClass($metadata->discriminatorMap[$typeValue]);
96587 }
96588
96589 private function leaveScope(Context $context, $data)
96590 {
96591 if ($context instanceof SerializationContext) {
96592 $context->stopVisiting($data);
96593 } elseif ($context instanceof DeserializationContext) {
96594 $context->decreaseDepth();
96595 }
96596 }
96597
96598 private function afterVisitingObject(ClassMetadata $metadata, $object, array $type, Context $context)
96599 {
96600 $this->leaveScope($context, $object);
96601 $context->popClassMetadata();
96602
96603 if ($context instanceof SerializationContext) {
96604 foreach ($metadata->postSerializeMethods as $method) {
96605 $method->invoke($object);
96606 }
96607
96608 if (null !== $this->dispatcher && $this->dispatcher->hasListeners('serializer.post_serialize', $metadata->name, $context->getFormat())) {
96609 $this->dispatcher->dispatch('serializer.post_serialize', $metadata->name, $context->getFormat(), new ObjectEvent($context, $object, $type));
96610 }
96611
96612 return;
96613 }
96614
96615 foreach ($metadata->postDeserializeMethods as $method) {
96616 $method->invoke($object);
96617 }
96618
96619 if (null !== $this->dispatcher && $this->dispatcher->hasListeners('serializer.post_deserialize', $metadata->name, $context->getFormat())) {
96620 $this->dispatcher->dispatch('serializer.post_deserialize', $metadata->name, $context->getFormat(), new ObjectEvent($context, $object, $type));
96621 }
96622 }
96623 }
96624 <?php
96625
96626 /*
96627 * Copyright 2013 Johannes M. Schmitt <schmittjoh@gmail.com>
96628 *
96629 * Licensed under the Apache License, Version 2.0 (the "License");
96630 * you may not use this file except in compliance with the License.
96631 * You may obtain a copy of the License at
96632 *
96633 * http://www.apache.org/licenses/LICENSE-2.0
96634 *
96635 * Unless required by applicable law or agreed to in writing, software
96636 * distributed under the License is distributed on an "AS IS" BASIS,
96637 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
96638 * See the License for the specific language governing permissions and
96639 * limitations under the License.
96640 */
96641
96642 namespace JMS\Serializer;
96643
96644 use JMS\Serializer\Exception\XmlErrorException;
96645 use JMS\Serializer\Exception\LogicException;
96646 use JMS\Serializer\Exception\InvalidArgumentException;
96647 use JMS\Serializer\Exception\RuntimeException;
96648 use JMS\Serializer\Metadata\PropertyMetadata;
96649 use JMS\Serializer\Metadata\ClassMetadata;
96650
96651 class XmlDeserializationVisitor extends AbstractVisitor
96652 {
96653 private $objectStack;
96654 private $metadataStack;
96655 private $currentObject;
96656 private $currentMetadata;
96657 private $result;
96658 private $navigator;
96659 private $disableExternalEntities = true;
96660 private $doctypeWhitelist = array();
96661
96662 public function enableExternalEntities()
96663 {
96664 $this->disableExternalEntities = false;
96665 }
96666
96667 public function setNavigator(GraphNavigator $navigator)
96668 {
96669 $this->navigator = $navigator;
96670 $this->objectStack = new \SplStack;
96671 $this->metadataStack = new \SplStack;
96672 $this->result = null;
96673 }
96674
96675 public function getNavigator()
96676 {
96677 return $this->navigator;
96678 }
96679
96680 public function prepare($data)
96681 {
96682 $previous = libxml_use_internal_errors(true);
96683 $previousEntityLoaderState = libxml_disable_entity_loader($this->disableExternalEntities);
96684
96685 $dom = new \DOMDocument();
96686 $dom->loadXML($data);
96687 foreach ($dom->childNodes as $child) {
96688 if ($child->nodeType === XML_DOCUMENT_TYPE_NODE) {
96689 $internalSubset = str_replace(array("\n", "\r"), '', $child->internalSubset);
96690 if (!in_array($internalSubset, $this->doctypeWhitelist, true)) {
96691 throw new InvalidArgumentException(sprintf(
96692 'The document type "%s" is not allowed. If it is safe, you may add it to the whitelist configuration.',
96693 $internalSubset
96694 ));
96695 }
96696 }
96697 }
96698
96699 $doc = simplexml_load_string($data);
96700 libxml_use_internal_errors($previous);
96701 libxml_disable_entity_loader($previousEntityLoaderState);
96702
96703 if (false === $doc) {
96704 throw new XmlErrorException(libxml_get_last_error());
96705 }
96706
96707 return $doc;
96708 }
96709
96710 public function visitNull($data, array $type, Context $context)
96711 {
96712 return null;
96713 }
96714
96715 public function visitString($data, array $type, Context $context)
96716 {
96717 $data = (string) $data;
96718
96719 if (null === $this->result) {
96720 $this->result = $data;
96721 }
96722
96723 return $data;
96724 }
96725
96726 public function visitBoolean($data, array $type, Context $context)
96727 {
96728 $data = (string) $data;
96729
96730 if ('true' === $data) {
96731 $data = true;
96732 } elseif ('false' === $data) {
96733 $data = false;
96734 } else {
96735 throw new RuntimeException(sprintf('Could not convert data to boolean. Expected "true", or "false", but got %s.', json_encode($data)));
96736 }
96737
96738 if (null === $this->result) {
96739 $this->result = $data;
96740 }
96741
96742 return $data;
96743 }
96744
96745 public function visitInteger($data, array $type, Context $context)
96746 {
96747 $data = (integer) $data;
96748
96749 if (null === $this->result) {
96750 $this->result = $data;
96751 }
96752
96753 return $data;
96754 }
96755
96756 public function visitDouble($data, array $type, Context $context)
96757 {
96758 $data = (double) $data;
96759
96760 if (null === $this->result) {
96761 $this->result = $data;
96762 }
96763
96764 return $data;
96765 }
96766
96767 public function visitArray($data, array $type, Context $context)
96768 {
96769 $entryName = null !== $this->currentMetadata && $this->currentMetadata->xmlEntryName ? $this->currentMetadata->xmlEntryName : 'entry';
96770
96771 if ( ! isset($data->$entryName)) {
96772 if (null === $this->result) {
96773 return $this->result = array();
96774 }
96775
96776 return array();
96777 }
96778
96779 switch (count($type['params'])) {
96780 case 0:
96781 throw new RuntimeException(sprintf('The array type must be specified either as "array<T>", or "array<K,V>".'));
96782
96783 case 1:
96784 $result = array();
96785 if (null === $this->result) {
96786 $this->result = &$result;
96787 }
96788
96789 foreach ($data->$entryName as $v) {
96790 $result[] = $this->navigator->accept($v, $type['params'][0], $context);
96791 }
96792
96793 return $result;
96794
96795 case 2:
96796 if (null === $this->currentMetadata) {
96797 throw new RuntimeException('Maps are not supported on top-level without metadata.');
96798 }
96799
96800 list($keyType, $entryType) = $type['params'];
96801 $result = array();
96802 if (null === $this->result) {
96803 $this->result = &$result;
96804 }
96805
96806 foreach ($data->$entryName as $v) {
96807 if (!isset($v[$this->currentMetadata->xmlKeyAttribute])) {
96808 throw new RuntimeException(sprintf('The key attribute "%s" must be set for each entry of the map.', $this->currentMetadata->xmlKeyAttribute));
96809 }
96810
96811 $k = $this->navigator->accept($v[$this->currentMetadata->xmlKeyAttribute], $keyType, $context);
96812 $result[$k] = $this->navigator->accept($v, $entryType, $context);
96813 }
96814
96815 return $result;
96816
96817 default:
96818 throw new LogicException(sprintf('The array type does not support more than 2 parameters, but got %s.', json_encode($type['params'])));
96819 }
96820 }
96821
96822 public function startVisitingObject(ClassMetadata $metadata, $object, array $type, Context $context)
96823 {
96824 $this->setCurrentObject($object);
96825
96826 if (null === $this->result) {
96827 $this->result = $this->currentObject;
96828 }
96829 }
96830
96831 public function visitProperty(PropertyMetadata $metadata, $data, Context $context)
96832 {
96833 $name = $this->namingStrategy->translateName($metadata);
96834
96835 if (!$metadata->type) {
96836 throw new RuntimeException(sprintf('You must define a type for %s::$%s.', $metadata->reflection->class, $metadata->name));
96837 }
96838
96839 if ($metadata->xmlAttribute) {
96840 if (isset($data[$name])) {
96841 $v = $this->navigator->accept($data[$name], $metadata->type, $context);
96842 $metadata->reflection->setValue($this->currentObject, $v);
96843 }
96844
96845 return;
96846 }
96847
96848 if ($metadata->xmlValue) {
96849 $v = $this->navigator->accept($data, $metadata->type, $context);
96850 $metadata->reflection->setValue($this->currentObject, $v);
96851
96852 return;
96853 }
96854
96855 if ($metadata->xmlCollection) {
96856 $enclosingElem = $data;
96857 if (!$metadata->xmlCollectionInline && isset($data->$name)) {
96858 $enclosingElem = $data->$name;
96859 }
96860
96861 $this->setCurrentMetadata($metadata);
96862 $v = $this->navigator->accept($enclosingElem, $metadata->type, $context);
96863 $this->revertCurrentMetadata();
96864 $metadata->reflection->setValue($this->currentObject, $v);
96865
96866 return;
96867 }
96868
96869 if (!isset($data->$name)) {
96870 return;
96871 }
96872
96873 $v = $this->navigator->accept($data->$name, $metadata->type, $context);
96874
96875 if (null === $metadata->setter) {
96876 $metadata->reflection->setValue($this->currentObject, $v);
96877
96878 return;
96879 }
96880
96881 $this->currentObject->{$metadata->setter}($v);
96882 }
96883
96884 public function endVisitingObject(ClassMetadata $metadata, $data, array $type, Context $context)
96885 {
96886 $rs = $this->currentObject;
96887 $this->revertCurrentObject();
96888
96889 return $rs;
96890 }
96891
96892 public function setCurrentObject($object)
96893 {
96894 $this->objectStack->push($this->currentObject);
96895 $this->currentObject = $object;
96896 }
96897
96898 public function getCurrentObject()
96899 {
96900 return $this->currentObject;
96901 }
96902
96903 public function revertCurrentObject()
96904 {
96905 return $this->currentObject = $this->objectStack->pop();
96906 }
96907
96908 public function setCurrentMetadata(PropertyMetadata $metadata)
96909 {
96910 $this->metadataStack->push($this->currentMetadata);
96911 $this->currentMetadata = $metadata;
96912 }
96913
96914 public function getCurrentMetadata()
96915 {
96916 return $this->currentMetadata;
96917 }
96918
96919 public function revertCurrentMetadata()
96920 {
96921 return $this->currentMetadata = $this->metadataStack->pop();
96922 }
96923
96924 public function getResult()
96925 {
96926 return $this->result;
96927 }
96928
96929 /**
96930 * @param array<string> $doctypeWhitelist
96931 */
96932 public function setDoctypeWhitelist(array $doctypeWhitelist)
96933 {
96934 $this->doctypeWhitelist = $doctypeWhitelist;
96935 }
96936
96937 /**
96938 * @return array<string>
96939 */
96940 public function getDoctypeWhitelist()
96941 {
96942 return $this->doctypeWhitelist;
96943 }
96944 }
96945 <?php
96946
96947 /*
96948 * Copyright 2013 Johannes M. Schmitt <schmittjoh@gmail.com>
96949 *
96950 * Licensed under the Apache License, Version 2.0 (the "License");
96951 * you may not use this file except in compliance with the License.
96952 * You may obtain a copy of the License at
96953 *
96954 * http://www.apache.org/licenses/LICENSE-2.0
96955 *
96956 * Unless required by applicable law or agreed to in writing, software
96957 * distributed under the License is distributed on an "AS IS" BASIS,
96958 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
96959 * See the License for the specific language governing permissions and
96960 * limitations under the License.
96961 */
96962
96963 namespace JMS\Serializer;
96964
96965 /**
96966 * Serializer Interface.
96967 *
96968 * @author Johannes M. Schmitt <schmittjoh@gmail.com>
96969 */
96970 interface SerializerInterface
96971 {
96972 /**
96973 * Serializes the given data to the specified output format.
96974 *
96975 * @param object|array|scalar $data
96976 * @param string $format
96977 * @param Context $context
96978 *
96979 * @return string
96980 */
96981 public function serialize($data, $format, SerializationContext $context = null);
96982
96983 /**
96984 * Deserializes the given data to the specified type.
96985 *
96986 * @param string $data
96987 * @param string $type
96988 * @param string $format
96989 * @param Context $context
96990 *
96991 * @return object|array|scalar
96992 */
96993 public function deserialize($data, $type, $format, DeserializationContext $context = null);
96994 }
96995 <?php
96996
96997 /*
96998 * Copyright 2013 Johannes M. Schmitt <schmittjoh@gmail.com>
96999 *
97000 * Licensed under the Apache License, Version 2.0 (the "License");
97001 * you may not use this file except in compliance with the License.
97002 * You may obtain a copy of the License at
97003 *
97004 * http://www.apache.org/licenses/LICENSE-2.0
97005 *
97006 * Unless required by applicable law or agreed to in writing, software
97007 * distributed under the License is distributed on an "AS IS" BASIS,
97008 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
97009 * See the License for the specific language governing permissions and
97010 * limitations under the License.
97011 */
97012
97013 namespace JMS\Serializer;
97014
97015 use JMS\Serializer\Exception\LogicException;
97016 use JMS\Serializer\Exception\RuntimeException;
97017 use Metadata\MetadataFactoryInterface;
97018
97019 class SerializationContext extends Context
97020 {
97021 /** @var \SplObjectStorage */
97022 private $visitingSet;
97023
97024 /** @var \SplStack */
97025 private $visitingStack;
97026
97027 public static function create()
97028 {
97029 return new self();
97030 }
97031
97032 public function initialize($format, VisitorInterface $visitor, GraphNavigator $navigator, MetadataFactoryInterface $factory)
97033 {
97034 parent::initialize($format, $visitor, $navigator, $factory);
97035
97036 $this->visitingSet = new \SplObjectStorage();
97037 $this->visitingStack = new \SplStack();
97038 }
97039
97040 public function startVisiting($object)
97041 {
97042 $this->visitingSet->attach($object);
97043 $this->visitingStack->push($object);
97044 }
97045
97046 public function stopVisiting($object)
97047 {
97048 $this->visitingSet->detach($object);
97049 $poppedObject = $this->visitingStack->pop();
97050
97051 if ($object !== $poppedObject) {
97052 throw new RuntimeException('Context visitingStack not working well');
97053 }
97054 }
97055
97056 public function isVisiting($object)
97057 {
97058 if (! is_object($object)) {
97059 throw new LogicException('Expected object but got ' . gettype($object) . '. Do you have the wrong @Type mapping or could this be a Doctrine many-to-many relation?');
97060 }
97061 return $this->visitingSet->contains($object);
97062 }
97063
97064 public function getPath()
97065 {
97066 $path = array();
97067 foreach ($this->visitingStack as $obj) {
97068 $path[] = get_class($obj);
97069 }
97070
97071 if ( ! $path) {
97072 return null;
97073 }
97074
97075 return implode(' -> ', $path);
97076 }
97077
97078 public function getDirection()
97079 {
97080 return GraphNavigator::DIRECTION_SERIALIZATION;
97081 }
97082
97083 public function getDepth()
97084 {
97085 return $this->visitingStack->count();
97086 }
97087
97088 public function getObject()
97089 {
97090 return !$this->visitingStack->isEmpty() ? $this->visitingStack->top() : null;
97091 }
97092
97093 public function getVisitingStack()
97094 {
97095 return $this->visitingStack;
97096 }
97097
97098 public function getVisitingSet()
97099 {
97100 return $this->visitingSet;
97101 }
97102 }
97103 <?php
97104
97105 /*
97106 * Copyright 2013 Johannes M. Schmitt <schmittjoh@gmail.com>
97107 *
97108 * Licensed under the Apache License, Version 2.0 (the "License");
97109 * you may not use this file except in compliance with the License.
97110 * You may obtain a copy of the License at
97111 *
97112 * http://www.apache.org/licenses/LICENSE-2.0
97113 *
97114 * Unless required by applicable law or agreed to in writing, software
97115 * distributed under the License is distributed on an "AS IS" BASIS,
97116 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
97117 * See the License for the specific language governing permissions and
97118 * limitations under the License.
97119 */
97120
97121 namespace JMS\Serializer;
97122
97123 use JMS\Serializer\Naming\PropertyNamingStrategyInterface;
97124
97125 abstract class AbstractVisitor implements VisitorInterface
97126 {
97127 protected $namingStrategy;
97128
97129 public function __construct(PropertyNamingStrategyInterface $namingStrategy)
97130 {
97131 $this->namingStrategy = $namingStrategy;
97132 }
97133
97134 public function getNamingStrategy()
97135 {
97136 return $this->namingStrategy;
97137 }
97138
97139 public function prepare($data)
97140 {
97141 return $data;
97142 }
97143 }
97144 <?php
97145
97146 /*
97147 * Copyright 2013 Johannes M. Schmitt <schmittjoh@gmail.com>
97148 *
97149 * Licensed under the Apache License, Version 2.0 (the "License");
97150 * you may not use this file except in compliance with the License.
97151 * You may obtain a copy of the License at
97152 *
97153 * http://www.apache.org/licenses/LICENSE-2.0
97154 *
97155 * Unless required by applicable law or agreed to in writing, software
97156 * distributed under the License is distributed on an "AS IS" BASIS,
97157 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
97158 * See the License for the specific language governing permissions and
97159 * limitations under the License.
97160 */
97161
97162 namespace JMS\Serializer;
97163
97164 class DeserializationContext extends Context
97165 {
97166 private $depth = 0;
97167
97168 public static function create()
97169 {
97170 return new self();
97171 }
97172
97173 public function getDirection()
97174 {
97175 return GraphNavigator::DIRECTION_DESERIALIZATION;
97176 }
97177
97178 public function getDepth()
97179 {
97180 return $this->depth;
97181 }
97182
97183 public function increaseDepth()
97184 {
97185 $this->depth += 1;
97186 }
97187
97188 public function decreaseDepth()
97189 {
97190 if ($this->depth <= 0) {
97191 throw new \LogicException('Depth cannot be smaller than zero.');
97192 }
97193
97194 $this->depth -= 1;
97195 }
97196 }<?php
97197
97198 /*
97199 * Copyright 2013 Johannes M. Schmitt <schmittjoh@gmail.com>
97200 *
97201 * Licensed under the Apache License, Version 2.0 (the "License");
97202 * you may not use this file except in compliance with the License.
97203 * You may obtain a copy of the License at
97204 *
97205 * http://www.apache.org/licenses/LICENSE-2.0
97206 *
97207 * Unless required by applicable law or agreed to in writing, software
97208 * distributed under the License is distributed on an "AS IS" BASIS,
97209 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
97210 * See the License for the specific language governing permissions and
97211 * limitations under the License.
97212 */
97213
97214 namespace JMS\Serializer\EventDispatcher;
97215
97216 interface EventDispatcherInterface
97217 {
97218 /**
97219 * Returns whether there are listeners.
97220 *
97221 * @param string $eventName
97222 * @param string $class
97223 * @param string $format
97224 *
97225 * @return boolean
97226 */
97227 public function hasListeners($eventName, $class, $format);
97228
97229 /**
97230 * Dispatches an event.
97231 *
97232 * The listeners/subscribers are called in the same order in which they
97233 * were added to the dispatcher.
97234 *
97235 * @param string $eventName
97236 * @param string $class
97237 * @param string $format
97238 * @param Event $event
97239 * @return void
97240 */
97241 public function dispatch($eventName, $class, $format, Event $event);
97242
97243 /**
97244 * Adds a listener.
97245 *
97246 * @param string $eventName
97247 * @param callable $callable
97248 * @param string|null $class
97249 * @param string|null $format
97250 * @return void
97251 */
97252 public function addListener($eventName, $callable, $class = null, $format = null);
97253
97254 /**
97255 * Adds a subscribers.
97256 *
97257 * @param EventSubscriberInterface $subscriber
97258 * @return void
97259 */
97260 public function addSubscriber(EventSubscriberInterface $subscriber);
97261 }
97262 <?php
97263
97264 /*
97265 * Copyright 2013 Johannes M. Schmitt <schmittjoh@gmail.com>
97266 *
97267 * Licensed under the Apache License, Version 2.0 (the "License");
97268 * you may not use this file except in compliance with the License.
97269 * You may obtain a copy of the License at
97270 *
97271 * http://www.apache.org/licenses/LICENSE-2.0
97272 *
97273 * Unless required by applicable law or agreed to in writing, software
97274 * distributed under the License is distributed on an "AS IS" BASIS,
97275 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
97276 * See the License for the specific language governing permissions and
97277 * limitations under the License.
97278 */
97279
97280 namespace JMS\Serializer\EventDispatcher;
97281
97282 use JMS\Serializer\Context;
97283
97284 class ObjectEvent extends Event
97285 {
97286 private $object;
97287
97288 public function __construct(Context $context, $object, array $type)
97289 {
97290 parent::__construct($context, $type);
97291
97292 $this->object = $object;
97293 }
97294
97295 public function getObject()
97296 {
97297 return $this->object;
97298 }
97299 }<?php
97300
97301 /*
97302 * Copyright 2013 Johannes M. Schmitt <schmittjoh@gmail.com>
97303 *
97304 * Licensed under the Apache License, Version 2.0 (the "License");
97305 * you may not use this file except in compliance with the License.
97306 * You may obtain a copy of the License at
97307 *
97308 * http://www.apache.org/licenses/LICENSE-2.0
97309 *
97310 * Unless required by applicable law or agreed to in writing, software
97311 * distributed under the License is distributed on an "AS IS" BASIS,
97312 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
97313 * See the License for the specific language governing permissions and
97314 * limitations under the License.
97315 */
97316
97317 namespace JMS\Serializer\EventDispatcher;
97318
97319 use JMS\Serializer\Context;
97320
97321 class Event
97322 {
97323 protected $type;
97324 private $context;
97325
97326 public function __construct(Context $context, array $type)
97327 {
97328 $this->context = $context;
97329 $this->type = $type;
97330 }
97331
97332 public function getVisitor()
97333 {
97334 return $this->context->getVisitor();
97335 }
97336
97337 public function getContext()
97338 {
97339 return $this->context;
97340 }
97341
97342 public function getType()
97343 {
97344 return $this->type;
97345 }
97346 }
97347 <?php
97348
97349 /*
97350 * Copyright 2013 Johannes M. Schmitt <schmittjoh@gmail.com>
97351 *
97352 * Licensed under the Apache License, Version 2.0 (the "License");
97353 * you may not use this file except in compliance with the License.
97354 * You may obtain a copy of the License at
97355 *
97356 * http://www.apache.org/licenses/LICENSE-2.0
97357 *
97358 * Unless required by applicable law or agreed to in writing, software
97359 * distributed under the License is distributed on an "AS IS" BASIS,
97360 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
97361 * See the License for the specific language governing permissions and
97362 * limitations under the License.
97363 */
97364
97365 namespace JMS\Serializer\EventDispatcher;
97366 use JMS\Serializer\Exception\InvalidArgumentException;
97367
97368 /**
97369 * Light-weight event dispatcher.
97370 *
97371 * This implementation focuses primarily on performance, and dispatching
97372 * events for certain classes. It is not a general purpose event dispatcher.
97373 *
97374 * @author Johannes M. Schmitt <schmittjoh@gmail.com>
97375 */
97376 class EventDispatcher implements EventDispatcherInterface
97377 {
97378 private $listeners = array();
97379 private $classListeners = array();
97380
97381 public static function getDefaultMethodName($eventName)
97382 {
97383 return 'on'.str_replace(array('_', '.'), '', $eventName);
97384 }
97385
97386 /**
97387 * Sets the listeners.
97388 *
97389 * @param array $listeners
97390 */
97391 public function setListeners(array $listeners)
97392 {
97393 $this->listeners = $listeners;
97394 $this->classListeners = array();
97395 }
97396
97397 public function addListener($eventName, $callable, $class = null, $format = null)
97398 {
97399 $this->listeners[$eventName][] = array($callable, null === $class ? null : strtolower($class), $format);
97400 unset($this->classListeners[$eventName]);
97401 }
97402
97403 public function addSubscriber(EventSubscriberInterface $subscriber)
97404 {
97405 foreach ($subscriber->getSubscribedEvents() as $eventData) {
97406 if ( ! isset($eventData['event'])) {
97407 throw new InvalidArgumentException(sprintf('Each event must have a "event" key.'));
97408 }
97409
97410 $method = isset($eventData['method']) ? $eventData['method'] : self::getDefaultMethodName($eventData['event']);
97411 $class = isset($eventData['class']) ? strtolower($eventData['class']) : null;
97412 $format = isset($eventData['format']) ? $eventData['format'] : null;
97413 $this->listeners[$eventData['event']][] = array(array($subscriber, $method), $class, $format);
97414 unset($this->classListeners[$eventData['event']]);
97415 }
97416 }
97417
97418 public function hasListeners($eventName, $class, $format)
97419 {
97420 if ( ! isset($this->listeners[$eventName])) {
97421 return false;
97422 }
97423
97424 $loweredClass = strtolower($class);
97425 if ( ! isset($this->classListeners[$eventName][$loweredClass][$format])) {
97426 $this->classListeners[$eventName][$loweredClass][$format] = $this->initializeListeners($eventName, $loweredClass, $format);
97427 }
97428
97429 return !!$this->classListeners[$eventName][$loweredClass][$format];
97430 }
97431
97432 public function dispatch($eventName, $class, $format, Event $event)
97433 {
97434 if ( ! isset($this->listeners[$eventName])) {
97435 return;
97436 }
97437
97438 $loweredClass = strtolower($class);
97439 if ( ! isset($this->classListeners[$eventName][$loweredClass][$format])) {
97440 $this->classListeners[$eventName][$loweredClass][$format] = $this->initializeListeners($eventName, $loweredClass, $format);
97441 }
97442
97443 foreach ($this->classListeners[$eventName][$loweredClass][$format] as $listener) {
97444 call_user_func($listener, $event);
97445 }
97446 }
97447
97448 /**
97449 * @param string $eventName
97450 * @param string $loweredClass
97451 * @param string $format
97452 *
97453 * @return array An array of listeners
97454 */
97455 protected function initializeListeners($eventName, $loweredClass, $format)
97456 {
97457 $listeners = array();
97458 foreach ($this->listeners[$eventName] as $listener) {
97459 if (null !== $listener[1] && $loweredClass !== $listener[1]) {
97460 continue;
97461 }
97462 if (null !== $listener[2] && $format !== $listener[2]) {
97463 continue;
97464 }
97465
97466 $listeners[] = $listener[0];
97467 }
97468
97469 return $listeners;
97470 }
97471 }
97472 <?php
97473
97474 /*
97475 * Copyright 2013 Johannes M. Schmitt <schmittjoh@gmail.com>
97476 *
97477 * Licensed under the Apache License, Version 2.0 (the "License");
97478 * you may not use this file except in compliance with the License.
97479 * You may obtain a copy of the License at
97480 *
97481 * http://www.apache.org/licenses/LICENSE-2.0
97482 *
97483 * Unless required by applicable law or agreed to in writing, software
97484 * distributed under the License is distributed on an "AS IS" BASIS,
97485 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
97486 * See the License for the specific language governing permissions and
97487 * limitations under the License.
97488 */
97489
97490 namespace JMS\Serializer\EventDispatcher;
97491
97492 abstract class Events
97493 {
97494 const PRE_SERIALIZE = 'serializer.pre_serialize';
97495 const POST_SERIALIZE = 'serializer.post_serialize';
97496 const PRE_DESERIALIZE = 'serializer.pre_deserialize';
97497 const POST_DESERIALIZE = 'serializer.post_deserialize';
97498
97499 final private function __construct() { }
97500 }
97501 <?php
97502
97503 /*
97504 * Copyright 2013 Johannes M. Schmitt <schmittjoh@gmail.com>
97505 *
97506 * Licensed under the Apache License, Version 2.0 (the "License");
97507 * you may not use this file except in compliance with the License.
97508 * You may obtain a copy of the License at
97509 *
97510 * http://www.apache.org/licenses/LICENSE-2.0
97511 *
97512 * Unless required by applicable law or agreed to in writing, software
97513 * distributed under the License is distributed on an "AS IS" BASIS,
97514 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
97515 * See the License for the specific language governing permissions and
97516 * limitations under the License.
97517 */
97518
97519 namespace JMS\Serializer\EventDispatcher;
97520
97521 use JMS\Serializer\DeserializationContext;
97522
97523 class PreDeserializeEvent extends Event
97524 {
97525 private $data;
97526
97527 public function __construct(DeserializationContext $context, $data, array $type)
97528 {
97529 parent::__construct($context, $type);
97530
97531 $this->data = $data;
97532 }
97533
97534 public function setType($name, array $params = array())
97535 {
97536 $this->type = array('name' => $name, 'params' => $params);
97537 }
97538
97539 public function getData()
97540 {
97541 return $this->data;
97542 }
97543
97544 public function setData($data)
97545 {
97546 $this->data = $data;
97547 }
97548 }<?php
97549
97550 /*
97551 * Copyright 2013 Johannes M. Schmitt <schmittjoh@gmail.com>
97552 *
97553 * Licensed under the Apache License, Version 2.0 (the "License");
97554 * you may not use this file except in compliance with the License.
97555 * You may obtain a copy of the License at
97556 *
97557 * http://www.apache.org/licenses/LICENSE-2.0
97558 *
97559 * Unless required by applicable law or agreed to in writing, software
97560 * distributed under the License is distributed on an "AS IS" BASIS,
97561 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
97562 * See the License for the specific language governing permissions and
97563 * limitations under the License.
97564 */
97565
97566 namespace JMS\Serializer\EventDispatcher\Subscriber;
97567
97568 use JMS\Serializer\EventDispatcher\Event;
97569 use JMS\Serializer\EventDispatcher\EventSubscriberInterface;
97570 use JMS\Serializer\Exception\ValidationFailedException;
97571 use Symfony\Component\Validator\ValidatorInterface;
97572
97573 class SymfonyValidatorSubscriber implements EventSubscriberInterface
97574 {
97575 private $validator;
97576
97577 public function __construct(ValidatorInterface $validator)
97578 {
97579 $this->validator = $validator;
97580 }
97581
97582 public static function getSubscribedEvents()
97583 {
97584 return array(
97585 array('event' => 'serializer.post_deserialize', 'method' => 'onPostDeserialize'),
97586 );
97587 }
97588
97589 public function onPostDeserialize(Event $event)
97590 {
97591 $context = $event->getContext();
97592
97593 if ($context->getDepth() > 0) {
97594 return;
97595 }
97596
97597 $validator = $this->validator;
97598 $context->attributes->get('validation_groups')->map(
97599 function(array $groups) use ($event, $validator) {
97600 $list = $validator->validate($event->getObject(), $groups);
97601
97602 if ($list->count() > 0) {
97603 throw new ValidationFailedException($list);
97604 }
97605 }
97606 );
97607 }
97608 }<?php
97609
97610 /*
97611 * Copyright 2013 Johannes M. Schmitt <schmittjoh@gmail.com>
97612 *
97613 * Licensed under the Apache License, Version 2.0 (the "License");
97614 * you may not use this file except in compliance with the License.
97615 * You may obtain a copy of the License at
97616 *
97617 * http://www.apache.org/licenses/LICENSE-2.0
97618 *
97619 * Unless required by applicable law or agreed to in writing, software
97620 * distributed under the License is distributed on an "AS IS" BASIS,
97621 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
97622 * See the License for the specific language governing permissions and
97623 * limitations under the License.
97624 */
97625
97626 namespace JMS\Serializer\EventDispatcher\Subscriber;
97627
97628 use Doctrine\ORM\PersistentCollection;
97629 use Doctrine\Common\Persistence\Proxy;
97630 use Doctrine\ORM\Proxy\Proxy as ORMProxy;
97631 use JMS\Serializer\EventDispatcher\PreSerializeEvent;
97632 use JMS\Serializer\EventDispatcher\EventSubscriberInterface;
97633
97634 class DoctrineProxySubscriber implements EventSubscriberInterface
97635 {
97636 public function onPreSerialize(PreSerializeEvent $event)
97637 {
97638 $object = $event->getObject();
97639 $type = $event->getType();
97640
97641 // If the set type name is not an actual class, but a faked type for which a custom handler exists, we do not
97642 // modify it with this subscriber. Also, we forgo autoloading here as an instance of this type is already created,
97643 // so it must be loaded if its a real class.
97644 $virtualType = ! class_exists($type['name'], false);
97645
97646 if ($object instanceof PersistentCollection) {
97647 if ( ! $virtualType) {
97648 $event->setType('ArrayCollection');
97649 }
97650
97651 return;
97652 }
97653
97654 if ( ! $object instanceof Proxy && ! $object instanceof ORMProxy) {
97655 return;
97656 }
97657
97658 $object->__load();
97659
97660 if ( ! $virtualType) {
97661 $event->setType(get_parent_class($object));
97662 }
97663 }
97664
97665 public static function getSubscribedEvents()
97666 {
97667 return array(
97668 array('event' => 'serializer.pre_serialize', 'method' => 'onPreSerialize'),
97669 );
97670 }
97671 }
97672 <?php
97673
97674 /*
97675 * Copyright 2013 Johannes M. Schmitt <schmittjoh@gmail.com>
97676 *
97677 * Licensed under the Apache License, Version 2.0 (the "License");
97678 * you may not use this file except in compliance with the License.
97679 * You may obtain a copy of the License at
97680 *
97681 * http://www.apache.org/licenses/LICENSE-2.0
97682 *
97683 * Unless required by applicable law or agreed to in writing, software
97684 * distributed under the License is distributed on an "AS IS" BASIS,
97685 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
97686 * See the License for the specific language governing permissions and
97687 * limitations under the License.
97688 */
97689
97690 namespace JMS\Serializer\EventDispatcher;
97691
97692 class PreSerializeEvent extends ObjectEvent
97693 {
97694 /**
97695 * @param string $typeName
97696 * @param array $params
97697 */
97698 public function setType($typeName, array $params = array())
97699 {
97700 $this->type = array('name' => $typeName, 'params' => $params);
97701 }
97702 }
97703 <?php
97704
97705 /*
97706 * Copyright 2013 Johannes M. Schmitt <schmittjoh@gmail.com>
97707 *
97708 * Licensed under the Apache License, Version 2.0 (the "License");
97709 * you may not use this file except in compliance with the License.
97710 * You may obtain a copy of the License at
97711 *
97712 * http://www.apache.org/licenses/LICENSE-2.0
97713 *
97714 * Unless required by applicable law or agreed to in writing, software
97715 * distributed under the License is distributed on an "AS IS" BASIS,
97716 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
97717 * See the License for the specific language governing permissions and
97718 * limitations under the License.
97719 */
97720
97721 namespace JMS\Serializer\EventDispatcher;
97722
97723 interface EventSubscriberInterface
97724 {
97725 /**
97726 * Returns the events to which this class has subscribed.
97727 *
97728 * Return format:
97729 * array(
97730 * array('event' => 'the-event-name', 'method' => 'onEventName', 'class' => 'some-class', 'format' => 'json'),
97731 * array(...),
97732 * )
97733 *
97734 * The class may be omitted if the class wants to subscribe to events of all classes.
97735 * Same goes for the format key.
97736 *
97737 * @return array
97738 */
97739 public static function getSubscribedEvents();
97740 }
97741 <?php
97742
97743 /*
97744 * Copyright 2013 Johannes M. Schmitt <schmittjoh@gmail.com>
97745 *
97746 * Licensed under the Apache License, Version 2.0 (the "License");
97747 * you may not use this file except in compliance with the License.
97748 * You may obtain a copy of the License at
97749 *
97750 * http://www.apache.org/licenses/LICENSE-2.0
97751 *
97752 * Unless required by applicable law or agreed to in writing, software
97753 * distributed under the License is distributed on an "AS IS" BASIS,
97754 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
97755 * See the License for the specific language governing permissions and
97756 * limitations under the License.
97757 */
97758
97759 namespace JMS\Serializer\EventDispatcher;
97760
97761 use Symfony\Component\DependencyInjection\ContainerInterface;
97762
97763 class LazyEventDispatcher extends EventDispatcher
97764 {
97765 private $container;
97766
97767 public function __construct(ContainerInterface $container)
97768 {
97769 $this->container = $container;
97770 }
97771
97772 /**
97773 * {@inheritdoc}
97774 */
97775 protected function initializeListeners($eventName, $loweredClass, $format)
97776 {
97777 $listeners = parent::initializeListeners($eventName, $loweredClass, $format);
97778
97779 foreach ($listeners as &$listener) {
97780 if ( ! is_array($listener) || ! is_string($listener[0])) {
97781 continue;
97782 }
97783
97784 if ( ! $this->container->has($listener[0])) {
97785 continue;
97786 }
97787
97788 $listener[0] = $this->container->get($listener[0]);
97789 }
97790
97791 return $listeners;
97792 }
97793 }
97794 <?php
97795
97796 /*
97797 * Copyright 2013 Johannes M. Schmitt <schmittjoh@gmail.com>
97798 *
97799 * Licensed under the Apache License, Version 2.0 (the "License");
97800 * you may not use this file except in compliance with the License.
97801 * You may obtain a copy of the License at
97802 *
97803 * http://www.apache.org/licenses/LICENSE-2.0
97804 *
97805 * Unless required by applicable law or agreed to in writing, software
97806 * distributed under the License is distributed on an "AS IS" BASIS,
97807 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
97808 * See the License for the specific language governing permissions and
97809 * limitations under the License.
97810 */
97811
97812 namespace JMS\Serializer;
97813
97814 use JMS\Serializer\Exception\RuntimeException;
97815 use JMS\Serializer\Exclusion\DepthExclusionStrategy;
97816 use JMS\Serializer\Exclusion\DisjunctExclusionStrategy;
97817 use JMS\Serializer\Exclusion\ExclusionStrategyInterface;
97818 use JMS\Serializer\Exclusion\GroupsExclusionStrategy;
97819 use JMS\Serializer\Exclusion\VersionExclusionStrategy;
97820 use JMS\Serializer\Metadata\ClassMetadata;
97821 use JMS\Serializer\Metadata\PropertyMetadata;
97822 use Metadata\MetadataFactory;
97823 use Metadata\MetadataFactoryInterface;
97824 use PhpCollection\Map;
97825
97826 abstract class Context
97827 {
97828 /**
97829 * @var \PhpCollection\Map
97830 */
97831 public $attributes;
97832
97833 private $format;
97834
97835 /** @var VisitorInterface */
97836 private $visitor;
97837
97838 /** @var GraphNavigator */
97839 private $navigator;
97840
97841 /** @var MetadataFactory */
97842 private $metadataFactory;
97843
97844 /** @var ExclusionStrategyInterface */
97845 private $exclusionStrategy;
97846
97847 /** @var boolean */
97848 private $serializeNull;
97849
97850 private $initialized = false;
97851
97852 /** @var \SplStack */
97853 private $metadataStack;
97854
97855 public function __construct()
97856 {
97857 $this->attributes = new Map();
97858 }
97859
97860 public function initialize($format, VisitorInterface $visitor, GraphNavigator $navigator, MetadataFactoryInterface $factory)
97861 {
97862 if ($this->initialized) {
97863 throw new \LogicException('This context was already initialized, and cannot be re-used.');
97864 }
97865
97866 $this->initialized = true;
97867 $this->format = $format;
97868 $this->visitor = $visitor;
97869 $this->navigator = $navigator;
97870 $this->metadataFactory = $factory;
97871 $this->metadataStack = new \SplStack();
97872 }
97873
97874 public function accept($data, array $type = null)
97875 {
97876 return $this->navigator->accept($data, $type, $this);
97877 }
97878
97879 public function getMetadataFactory()
97880 {
97881 return $this->metadataFactory;
97882 }
97883
97884 public function getVisitor()
97885 {
97886 return $this->visitor;
97887 }
97888
97889 public function getNavigator()
97890 {
97891 return $this->navigator;
97892 }
97893
97894 public function getExclusionStrategy()
97895 {
97896 return $this->exclusionStrategy;
97897 }
97898
97899 public function setAttribute($key, $value)
97900 {
97901 $this->assertMutable();
97902 $this->attributes->set($key, $value);
97903
97904 return $this;
97905 }
97906
97907 private function assertMutable()
97908 {
97909 if ( ! $this->initialized) {
97910 return;
97911 }
97912
97913 throw new \LogicException('This context was already initialized and is immutable; you cannot modify it anymore.');
97914 }
97915
97916 public function addExclusionStrategy(ExclusionStrategyInterface $strategy)
97917 {
97918 $this->assertMutable();
97919
97920 if (null === $this->exclusionStrategy) {
97921 $this->exclusionStrategy = $strategy;
97922
97923 return $this;
97924 }
97925
97926 if ($this->exclusionStrategy instanceof DisjunctExclusionStrategy) {
97927 $this->exclusionStrategy->addStrategy($strategy);
97928
97929 return $this;
97930 }
97931
97932 $this->exclusionStrategy = new DisjunctExclusionStrategy(array(
97933 $this->exclusionStrategy,
97934 $strategy,
97935 ));
97936
97937 return $this;
97938 }
97939
97940 /**
97941 * @param integer $version
97942 */
97943 public function setVersion($version)
97944 {
97945 if (null === $version) {
97946 throw new \LogicException('The version must not be null.');
97947 }
97948
97949 $this->attributes->set('version', $version);
97950 $this->addExclusionStrategy(new VersionExclusionStrategy($version));
97951
97952 return $this;
97953 }
97954
97955 /**
97956 * @param array|string $groups
97957 */
97958 public function setGroups($groups)
97959 {
97960 if (empty($groups)) {
97961 throw new \LogicException('The groups must not be empty.');
97962 }
97963
97964 $this->attributes->set('groups', (array) $groups);
97965 $this->addExclusionStrategy(new GroupsExclusionStrategy((array) $groups));
97966
97967 return $this;
97968 }
97969
97970 public function enableMaxDepthChecks()
97971 {
97972 $this->addExclusionStrategy(new DepthExclusionStrategy());
97973
97974 return $this;
97975 }
97976
97977 public function setSerializeNull($bool)
97978 {
97979 $this->serializeNull = (boolean) $bool;
97980
97981 return $this;
97982 }
97983
97984 public function shouldSerializeNull()
97985 {
97986 return $this->serializeNull;
97987 }
97988
97989 public function getFormat()
97990 {
97991 return $this->format;
97992 }
97993
97994 public function pushClassMetadata(ClassMetadata $metadata)
97995 {
97996 $this->metadataStack->push($metadata);
97997 }
97998
97999 public function pushPropertyMetadata(PropertyMetadata $metadata)
98000 {
98001 $this->metadataStack->push($metadata);
98002 }
98003
98004 public function popPropertyMetadata()
98005 {
98006 $metadata = $this->metadataStack->pop();
98007
98008 if (!$metadata instanceof PropertyMetadata) {
98009 throw new RuntimeException('Context metadataStack not working well');
98010 }
98011 }
98012
98013 public function popClassMetadata()
98014 {
98015 $metadata = $this->metadataStack->pop();
98016
98017 if (!$metadata instanceof ClassMetadata) {
98018 throw new RuntimeException('Context metadataStack not working well');
98019 }
98020 }
98021
98022 public function getMetadataStack()
98023 {
98024 return $this->metadataStack;
98025 }
98026
98027 abstract public function getDepth();
98028 abstract public function getDirection();
98029 }
98030 <?php
98031
98032 /*
98033 * Copyright 2013 Johannes M. Schmitt <schmittjoh@gmail.com>
98034 *
98035 * Licensed under the Apache License, Version 2.0 (the "License");
98036 * you may not use this file except in compliance with the License.
98037 * You may obtain a copy of the License at
98038 *
98039 * http://www.apache.org/licenses/LICENSE-2.0
98040 *
98041 * Unless required by applicable law or agreed to in writing, software
98042 * distributed under the License is distributed on an "AS IS" BASIS,
98043 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
98044 * See the License for the specific language governing permissions and
98045 * limitations under the License.
98046 */
98047
98048 namespace JMS\Serializer;
98049
98050 use JMS\Serializer\Exception\RuntimeException;
98051 use JMS\Serializer\Metadata\ClassMetadata;
98052 use JMS\Serializer\Metadata\PropertyMetadata;
98053
98054 /**
98055 * XmlSerializationVisitor.
98056 *
98057 * @author Johannes M. Schmitt <schmittjoh@gmail.com>
98058 */
98059 class XmlSerializationVisitor extends AbstractVisitor
98060 {
98061 public $document;
98062
98063 private $navigator;
98064 private $defaultRootName = 'result';
98065 private $defaultVersion = '1.0';
98066 private $defaultEncoding = 'UTF-8';
98067 private $stack;
98068 private $metadataStack;
98069 private $currentNode;
98070 private $currentMetadata;
98071 private $hasValue;
98072 private $nullWasVisited;
98073
98074 public function setDefaultRootName($name)
98075 {
98076 $this->defaultRootName = $name;
98077 }
98078
98079 /**
98080 * @return boolean
98081 */
98082 public function hasDefaultRootName()
98083 {
98084 return 'result' === $this->defaultRootName;
98085 }
98086
98087 public function setDefaultVersion($version)
98088 {
98089 $this->defaultVersion = $version;
98090 }
98091
98092 public function setDefaultEncoding($encoding)
98093 {
98094 $this->defaultEncoding = $encoding;
98095 }
98096
98097 public function setNavigator(GraphNavigator $navigator)
98098 {
98099 $this->navigator = $navigator;
98100 $this->document = null;
98101 $this->stack = new \SplStack;
98102 $this->metadataStack = new \SplStack;
98103 }
98104
98105 public function getNavigator()
98106 {
98107 return $this->navigator;
98108 }
98109
98110 public function visitNull($data, array $type, Context $context)
98111 {
98112 if (null === $this->document) {
98113 $this->document = $this->createDocument(null, null, true);
98114 $node = $this->document->createAttribute('xsi:nil');
98115 $node->value = 'true';
98116 $this->currentNode->appendChild($node);
98117
98118 $this->attachNullNamespace();
98119
98120 return;
98121 }
98122
98123 $node = $this->document->createAttribute('xsi:nil');
98124 $node->value = 'true';
98125 $this->attachNullNamespace();
98126
98127 return $node;
98128 }
98129
98130 public function visitString($data, array $type, Context $context)
98131 {
98132 if (null === $this->document) {
98133 $this->document = $this->createDocument(null, null, true);
98134 $this->currentNode->appendChild($this->document->createCDATASection($data));
98135
98136 return;
98137 }
98138
98139 return $this->document->createCDATASection($data);
98140 }
98141
98142 public function visitBoolean($data, array $type, Context $context)
98143 {
98144 if (null === $this->document) {
98145 $this->document = $this->createDocument(null, null, true);
98146 $this->currentNode->appendChild($this->document->createTextNode($data ? 'true' : 'false'));
98147
98148 return;
98149 }
98150
98151 return $this->document->createTextNode($data ? 'true' : 'false');
98152 }
98153
98154 public function visitInteger($data, array $type, Context $context)
98155 {
98156 return $this->visitNumeric($data, $type);
98157 }
98158
98159 public function visitDouble($data, array $type, Context $context)
98160 {
98161 return $this->visitNumeric($data, $type);
98162 }
98163
98164 public function visitArray($data, array $type, Context $context)
98165 {
98166 if (null === $this->document) {
98167 $this->document = $this->createDocument(null, null, true);
98168 }
98169
98170 $entryName = (null !== $this->currentMetadata && null !== $this->currentMetadata->xmlEntryName) ? $this->currentMetadata->xmlEntryName : 'entry';
98171 $keyAttributeName = (null !== $this->currentMetadata && null !== $this->currentMetadata->xmlKeyAttribute) ? $this->currentMetadata->xmlKeyAttribute : null;
98172
98173 foreach ($data as $k => $v) {
98174 $tagName = (null !== $this->currentMetadata && $this->currentMetadata->xmlKeyValuePairs && $this->isElementNameValid($k)) ? $k : $entryName;
98175
98176 $entryNode = $this->document->createElement($tagName);
98177 $this->currentNode->appendChild($entryNode);
98178 $this->setCurrentNode($entryNode);
98179
98180 if (null !== $keyAttributeName) {
98181 $entryNode->setAttribute($keyAttributeName, (string) $k);
98182 }
98183
98184 if (null !== $node = $this->navigator->accept($v, isset($type['params'][1]) ? $type['params'][1] : null, $context)) {
98185 $this->currentNode->appendChild($node);
98186 }
98187
98188 $this->revertCurrentNode();
98189 }
98190 }
98191
98192 public function startVisitingObject(ClassMetadata $metadata, $data, array $type, Context $context)
98193 {
98194 if (null === $this->document) {
98195 $this->document = $this->createDocument(null, null, false);
98196 $this->document->appendChild($this->currentNode = $this->document->createElement($metadata->xmlRootName ?: $this->defaultRootName));
98197 }
98198
98199 $this->hasValue = false;
98200 }
98201
98202 public function visitProperty(PropertyMetadata $metadata, $object, Context $context)
98203 {
98204 $v = $metadata->getValue($object);
98205
98206 if (null === $v && !$context->shouldSerializeNull()) {
98207 return;
98208 }
98209
98210 if ($metadata->xmlAttribute) {
98211 $node = $this->navigator->accept($v, $metadata->type, $context);
98212 if (!$node instanceof \DOMCharacterData) {
98213 throw new RuntimeException(sprintf('Unsupported value for XML attribute. Expected character data, but got %s.', json_encode($v)));
98214 }
98215
98216 $this->currentNode->setAttribute($this->namingStrategy->translateName($metadata), $node->nodeValue);
98217
98218 return;
98219 }
98220
98221 if (($metadata->xmlValue && $this->currentNode->childNodes->length > 0)
98222 || (!$metadata->xmlValue && $this->hasValue)) {
98223 throw new RuntimeException(sprintf('If you make use of @XmlValue, all other properties in the class must have the @XmlAttribute annotation. Invalid usage detected in class %s.', $metadata->class));
98224 }
98225
98226 if ($metadata->xmlValue) {
98227 $this->hasValue = true;
98228
98229 $node = $this->navigator->accept($v, $metadata->type, $context);
98230 if (!$node instanceof \DOMCharacterData) {
98231 throw new RuntimeException(sprintf('Unsupported value for property %s::$%s. Expected character data, but got %s.', $metadata->reflection->class, $metadata->reflection->name, is_object($node) ? get_class($node) : gettype($node)));
98232 }
98233
98234 $this->currentNode->appendChild($node);
98235
98236 return;
98237 }
98238
98239 if ($metadata->xmlAttributeMap) {
98240 if (!is_array($v)) {
98241 throw new RuntimeException(sprintf('Unsupported value type for XML attribute map. Expected array but got %s.', gettype($v)));
98242 }
98243
98244 foreach ($v as $key => $value) {
98245 $node = $this->navigator->accept($value, null, $context);
98246 if (!$node instanceof \DOMCharacterData) {
98247 throw new RuntimeException(sprintf('Unsupported value for a XML attribute map value. Expected character data, but got %s.', json_encode($v)));
98248 }
98249
98250 $this->currentNode->setAttribute($key, $node->nodeValue);
98251 }
98252
98253 return;
98254 }
98255
98256 if ($addEnclosingElement = (!$metadata->xmlCollection || !$metadata->xmlCollectionInline) && !$metadata->inline) {
98257 $element = $this->document->createElement($this->namingStrategy->translateName($metadata));
98258 $this->setCurrentNode($element);
98259 }
98260
98261 $this->setCurrentMetadata($metadata);
98262
98263 if (null !== $node = $this->navigator->accept($v, $metadata->type, $context)) {
98264 $this->currentNode->appendChild($node);
98265 }
98266
98267 $this->revertCurrentMetadata();
98268
98269 if ($addEnclosingElement) {
98270 $this->revertCurrentNode();
98271
98272 if ($element->hasChildNodes() || $element->hasAttributes()
98273 || (isset($metadata->type['name']) && $metadata->type['name'] === 'array' && isset($metadata->type['params'][1]))) {
98274 $this->currentNode->appendChild($element);
98275 }
98276 }
98277
98278 $this->hasValue = false;
98279 }
98280
98281 public function endVisitingObject(ClassMetadata $metadata, $data, array $type, Context $context)
98282 {
98283 }
98284
98285 public function getResult()
98286 {
98287 return $this->document->saveXML();
98288 }
98289
98290 public function getCurrentNode()
98291 {
98292 return $this->currentNode;
98293 }
98294
98295 public function getCurrentMetadata()
98296 {
98297 return $this->currentMetadata;
98298 }
98299
98300 public function getDocument()
98301 {
98302 return $this->document;
98303 }
98304
98305 public function setCurrentMetadata(PropertyMetadata $metadata)
98306 {
98307 $this->metadataStack->push($this->currentMetadata);
98308 $this->currentMetadata = $metadata;
98309 }
98310
98311 public function setCurrentNode(\DOMNode $node)
98312 {
98313 $this->stack->push($this->currentNode);
98314 $this->currentNode = $node;
98315 }
98316
98317 public function revertCurrentNode()
98318 {
98319 return $this->currentNode = $this->stack->pop();
98320 }
98321
98322 public function revertCurrentMetadata()
98323 {
98324 return $this->currentMetadata = $this->metadataStack->pop();
98325 }
98326
98327 public function createDocument($version = null, $encoding = null, $addRoot = true)
98328 {
98329 $doc = new \DOMDocument($version ?: $this->defaultVersion, $encoding ?: $this->defaultEncoding);
98330 $doc->formatOutput = true;
98331
98332 if ($addRoot) {
98333 $this->setCurrentNode($rootNode = $doc->createElement($this->defaultRootName));
98334 $doc->appendChild($rootNode);
98335 }
98336
98337 return $doc;
98338 }
98339
98340 public function prepare($data)
98341 {
98342 $this->nullWasVisited = false;
98343
98344 return $data;
98345 }
98346
98347 private function visitNumeric($data, array $type)
98348 {
98349 if (null === $this->document) {
98350 $this->document = $this->createDocument(null, null, true);
98351 $this->currentNode->appendChild($textNode = $this->document->createTextNode((string) $data));
98352
98353 return $textNode;
98354 }
98355
98356 return $this->document->createTextNode((string) $data);
98357 }
98358
98359 /**
98360 * Checks that the name is a valid XML element name.
98361 *
98362 * @param string $name
98363 *
98364 * @return boolean
98365 */
98366 private function isElementNameValid($name)
98367 {
98368 return $name && false === strpos($name, ' ') && preg_match('#^[\pL_][\pL0-9._-]*$#ui', $name);
98369 }
98370
98371 private function attachNullNamespace()
98372 {
98373 if (!$this->nullWasVisited) {
98374 $this->document->documentElement->setAttributeNS(
98375 'http://www.w3.org/2000/xmlns/',
98376 'xmlns:xsi',
98377 'http://www.w3.org/2001/XMLSchema-instance'
98378 );
98379 $this->nullWasVisited = true;
98380 }
98381 }
98382 }
98383 <?php
98384
98385 /*
98386 * Copyright 2013 Johannes M. Schmitt <schmittjoh@gmail.com>
98387 *
98388 * Licensed under the Apache License, Version 2.0 (the "License");
98389 * you may not use this file except in compliance with the License.
98390 * You may obtain a copy of the License at
98391 *
98392 * http://www.apache.org/licenses/LICENSE-2.0
98393 *
98394 * Unless required by applicable law or agreed to in writing, software
98395 * distributed under the License is distributed on an "AS IS" BASIS,
98396 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
98397 * See the License for the specific language governing permissions and
98398 * limitations under the License.
98399 */
98400
98401 namespace JMS\Serializer\Construction;
98402
98403 use JMS\Serializer\VisitorInterface;
98404 use JMS\Serializer\Metadata\ClassMetadata;
98405
98406 /**
98407 * Implementations of this interface construct new objects during deserialization.
98408 *
98409 * @author Johannes M. Schmitt <schmittjoh@gmail.com>
98410 */
98411 interface ObjectConstructorInterface
98412 {
98413 /**
98414 * Constructs a new object.
98415 *
98416 * Implementations could for example create a new object calling "new", use
98417 * "unserialize" techniques, reflection, or other means.
98418 *
98419 * @param VisitorInterface $visitor
98420 * @param ClassMetadata $metadata
98421 * @param mixed $data
98422 * @param array $type ["name" => string, "params" => array]
98423 *
98424 * @return object
98425 */
98426 public function construct(VisitorInterface $visitor, ClassMetadata $metadata, $data, array $type);
98427 }
98428 <?php
98429
98430 /*
98431 * Copyright 2013 Johannes M. Schmitt <schmittjoh@gmail.com>
98432 *
98433 * Licensed under the Apache License, Version 2.0 (the "License");
98434 * you may not use this file except in compliance with the License.
98435 * You may obtain a copy of the License at
98436 *
98437 * http://www.apache.org/licenses/LICENSE-2.0
98438 *
98439 * Unless required by applicable law or agreed to in writing, software
98440 * distributed under the License is distributed on an "AS IS" BASIS,
98441 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
98442 * See the License for the specific language governing permissions and
98443 * limitations under the License.
98444 */
98445
98446 namespace JMS\Serializer\Construction;
98447
98448 use Doctrine\Common\Persistence\ManagerRegistry;
98449 use JMS\Serializer\VisitorInterface;
98450 use JMS\Serializer\Metadata\ClassMetadata;
98451
98452 /**
98453 * Doctrine object constructor for new (or existing) objects during deserialization.
98454 */
98455 class DoctrineObjectConstructor implements ObjectConstructorInterface
98456 {
98457 private $managerRegistry;
98458 private $fallbackConstructor;
98459
98460 /**
98461 * Constructor.
98462 *
98463 * @param ManagerRegistry $managerRegistry Manager registry
98464 * @param ObjectConstructorInterface $fallbackConstructor Fallback object constructor
98465 */
98466 public function __construct(ManagerRegistry $managerRegistry, ObjectConstructorInterface $fallbackConstructor)
98467 {
98468 $this->managerRegistry = $managerRegistry;
98469 $this->fallbackConstructor = $fallbackConstructor;
98470 }
98471
98472 /**
98473 * {@inheritdoc}
98474 */
98475 public function construct(VisitorInterface $visitor, ClassMetadata $metadata, $data, array $type)
98476 {
98477 // Locate possible ObjectManager
98478 $objectManager = $this->managerRegistry->getManagerForClass($metadata->name);
98479
98480 if (!$objectManager) {
98481 // No ObjectManager found, proceed with normal deserialization
98482 return $this->fallbackConstructor->construct($visitor, $metadata, $data, $type);
98483 }
98484
98485 // Locate possible ClassMetadata
98486 $classMetadataFactory = $objectManager->getMetadataFactory();
98487
98488 if ($classMetadataFactory->isTransient($metadata->name)) {
98489 // No ClassMetadata found, proceed with normal deserialization
98490 return $this->fallbackConstructor->construct($visitor, $metadata, $data, $type);
98491 }
98492
98493 // Managed entity, check for proxy load
98494 if (!is_array($data)) {
98495 // Single identifier, load proxy
98496 return $objectManager->getReference($metadata->name, $data);
98497 }
98498
98499 // Fallback to default constructor if missing identifier(s)
98500 $classMetadata = $objectManager->getClassMetadata($metadata->name);
98501 $identifierList = array();
98502
98503 foreach ($classMetadata->getIdentifierFieldNames() as $name) {
98504 if ( ! array_key_exists($name, $data)) {
98505 return $this->fallbackConstructor->construct($visitor, $metadata, $data, $type);
98506 }
98507
98508 $identifierList[$name] = $data[$name];
98509 }
98510
98511 // Entity update, load it from database
98512 $object = $objectManager->find($metadata->name, $identifierList);
98513
98514 $objectManager->initializeObject($object);
98515
98516 return $object;
98517 }
98518 }
98519 <?php
98520
98521 /*
98522 * Copyright 2013 Johannes M. Schmitt <schmittjoh@gmail.com>
98523 *
98524 * Licensed under the Apache License, Version 2.0 (the "License");
98525 * you may not use this file except in compliance with the License.
98526 * You may obtain a copy of the License at
98527 *
98528 * http://www.apache.org/licenses/LICENSE-2.0
98529 *
98530 * Unless required by applicable law or agreed to in writing, software
98531 * distributed under the License is distributed on an "AS IS" BASIS,
98532 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
98533 * See the License for the specific language governing permissions and
98534 * limitations under the License.
98535 */
98536
98537 namespace JMS\Serializer\Construction;
98538
98539 use JMS\Serializer\VisitorInterface;
98540 use JMS\Serializer\Metadata\ClassMetadata;
98541
98542 class UnserializeObjectConstructor implements ObjectConstructorInterface
98543 {
98544 public function construct(VisitorInterface $visitor, ClassMetadata $metadata, $data, array $type)
98545 {
98546 return unserialize(sprintf('O:%d:"%s":0:{}', strlen($metadata->name), $metadata->name));
98547 }
98548 }
98549 <?php
98550
98551 /*
98552 * Copyright 2013 Johannes M. Schmitt <schmittjoh@gmail.com>
98553 *
98554 * Licensed under the Apache License, Version 2.0 (the "License");
98555 * you may not use this file except in compliance with the License.
98556 * You may obtain a copy of the License at
98557 *
98558 * http://www.apache.org/licenses/LICENSE-2.0
98559 *
98560 * Unless required by applicable law or agreed to in writing, software
98561 * distributed under the License is distributed on an "AS IS" BASIS,
98562 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
98563 * See the License for the specific language governing permissions and
98564 * limitations under the License.
98565 */
98566
98567 namespace JMS\Serializer\Exception;
98568
98569 /**
98570 * Base exception for the Serializer.
98571 *
98572 * @author Johannes M. Schmitt <schmittjoh@gmail.com>
98573 */
98574 interface Exception
98575 {
98576 }
98577 <?php
98578
98579 /*
98580 * Copyright 2013 Johannes M. Schmitt <schmittjoh@gmail.com>
98581 *
98582 * Licensed under the Apache License, Version 2.0 (the "License");
98583 * you may not use this file except in compliance with the License.
98584 * You may obtain a copy of the License at
98585 *
98586 * http://www.apache.org/licenses/LICENSE-2.0
98587 *
98588 * Unless required by applicable law or agreed to in writing, software
98589 * distributed under the License is distributed on an "AS IS" BASIS,
98590 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
98591 * See the License for the specific language governing permissions and
98592 * limitations under the License.
98593 */
98594
98595 namespace JMS\Serializer\Exception;
98596
98597 /**
98598 * RuntimeException for the Serializer.
98599 *
98600 * @author Johannes M. Schmitt <schmittjoh@gmail.com>
98601 */
98602 class RuntimeException extends \RuntimeException implements Exception
98603 {
98604 }
98605 <?php
98606
98607 /*
98608 * Copyright 2013 Johannes M. Schmitt <schmittjoh@gmail.com>
98609 *
98610 * Licensed under the Apache License, Version 2.0 (the "License");
98611 * you may not use this file except in compliance with the License.
98612 * You may obtain a copy of the License at
98613 *
98614 * http://www.apache.org/licenses/LICENSE-2.0
98615 *
98616 * Unless required by applicable law or agreed to in writing, software
98617 * distributed under the License is distributed on an "AS IS" BASIS,
98618 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
98619 * See the License for the specific language governing permissions and
98620 * limitations under the License.
98621 */
98622
98623 namespace JMS\Serializer\Exception;
98624
98625 /**
98626 * InvalidArgumentException for the Serializer.
98627 *
98628 * @author Johannes M. Schmitt <schmittjoh@gmail.com>
98629 */
98630 class InvalidArgumentException extends \InvalidArgumentException implements Exception
98631 {
98632 }
98633 <?php
98634
98635 /*
98636 * Copyright 2013 Johannes M. Schmitt <schmittjoh@gmail.com>
98637 *
98638 * Licensed under the Apache License, Version 2.0 (the "License");
98639 * you may not use this file except in compliance with the License.
98640 * You may obtain a copy of the License at
98641 *
98642 * http://www.apache.org/licenses/LICENSE-2.0
98643 *
98644 * Unless required by applicable law or agreed to in writing, software
98645 * distributed under the License is distributed on an "AS IS" BASIS,
98646 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
98647 * See the License for the specific language governing permissions and
98648 * limitations under the License.
98649 */
98650
98651 namespace JMS\Serializer\Exception;
98652
98653 /**
98654 * LogicException for the Serializer.
98655 *
98656 * @author Johannes M. Schmitt <schmittjoh@gmail.com>
98657 */
98658 class LogicException extends \LogicException implements Exception
98659 {
98660 }
98661 <?php
98662
98663 /*
98664 * Copyright 2013 Johannes M. Schmitt <schmittjoh@gmail.com>
98665 *
98666 * Licensed under the Apache License, Version 2.0 (the "License");
98667 * you may not use this file except in compliance with the License.
98668 * You may obtain a copy of the License at
98669 *
98670 * http://www.apache.org/licenses/LICENSE-2.0
98671 *
98672 * Unless required by applicable law or agreed to in writing, software
98673 * distributed under the License is distributed on an "AS IS" BASIS,
98674 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
98675 * See the License for the specific language governing permissions and
98676 * limitations under the License.
98677 */
98678
98679 namespace JMS\Serializer\Exception;
98680
98681 use Symfony\Component\Validator\ConstraintViolationList;
98682
98683 class ValidationFailedException extends RuntimeException
98684 {
98685 private $list;
98686
98687 public function __construct(ConstraintViolationList $list)
98688 {
98689 parent::__construct(sprintf('Validation failed with %d error(s).', count($list)));
98690
98691 $this->list = $list;
98692 }
98693
98694 public function getConstraintViolationList()
98695 {
98696 return $this->list;
98697 }
98698 }<?php
98699
98700 /*
98701 * Copyright 2013 Johannes M. Schmitt <schmittjoh@gmail.com>
98702 *
98703 * Licensed under the Apache License, Version 2.0 (the "License");
98704 * you may not use this file except in compliance with the License.
98705 * You may obtain a copy of the License at
98706 *
98707 * http://www.apache.org/licenses/LICENSE-2.0
98708 *
98709 * Unless required by applicable law or agreed to in writing, software
98710 * distributed under the License is distributed on an "AS IS" BASIS,
98711 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
98712 * See the License for the specific language governing permissions and
98713 * limitations under the License.
98714 */
98715
98716 namespace JMS\Serializer\Exception;
98717
98718 class XmlErrorException extends RuntimeException
98719 {
98720 private $xmlError;
98721
98722 public function __construct(\LibXMLError $error)
98723 {
98724 switch ($error->level) {
98725 case LIBXML_ERR_WARNING:
98726 $level = 'WARNING';
98727 break;
98728
98729 case LIBXML_ERR_FATAL:
98730 $level = 'FATAL';
98731 break;
98732
98733 case LIBXML_ERR_ERROR:
98734 $level = 'ERROR';
98735 break;
98736
98737 default:
98738 $level = 'UNKNOWN';
98739 }
98740
98741 parent::__construct(sprintf('[%s] %s in %s (line: %d, column: %d)', $level, $error->message, $error->file, $error->line, $error->column));
98742
98743 $this->xmlError = $error;
98744 }
98745
98746 public function getXmlError()
98747 {
98748 return $this->xmlError;
98749 }
98750 }
98751 <?php
98752
98753 /*
98754 * Copyright 2013 Johannes M. Schmitt <schmittjoh@gmail.com>
98755 *
98756 * Licensed under the Apache License, Version 2.0 (the "License");
98757 * you may not use this file except in compliance with the License.
98758 * You may obtain a copy of the License at
98759 *
98760 * http://www.apache.org/licenses/LICENSE-2.0
98761 *
98762 * Unless required by applicable law or agreed to in writing, software
98763 * distributed under the License is distributed on an "AS IS" BASIS,
98764 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
98765 * See the License for the specific language governing permissions and
98766 * limitations under the License.
98767 */
98768
98769 namespace JMS\Serializer\Exception;
98770
98771 class UnsupportedFormatException extends InvalidArgumentException
98772 {
98773 }
98774 <?php
98775
98776 /*
98777 * Copyright 2013 Johannes M. Schmitt <schmittjoh@gmail.com>
98778 *
98779 * Licensed under the Apache License, Version 2.0 (the "License");
98780 * you may not use this file except in compliance with the License.
98781 * You may obtain a copy of the License at
98782 *
98783 * http://www.apache.org/licenses/LICENSE-2.0
98784 *
98785 * Unless required by applicable law or agreed to in writing, software
98786 * distributed under the License is distributed on an "AS IS" BASIS,
98787 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
98788 * See the License for the specific language governing permissions and
98789 * limitations under the License.
98790 */
98791
98792 namespace JMS\Serializer\Util;
98793
98794 use JMS\Serializer\Exception\RuntimeException;
98795
98796 /**
98797 * A writer implementation.
98798 *
98799 * This may be used to simplify writing well-formatted code.
98800 *
98801 * @author Johannes M. Schmitt <schmittjoh@gmail.com>
98802 */
98803 class Writer
98804 {
98805 public $indentationSpaces = 4;
98806 public $indentationLevel = 0;
98807 public $content = '';
98808 public $changeCount = 0;
98809
98810 private $changes = array();
98811
98812 public function indent()
98813 {
98814 $this->indentationLevel += 1;
98815
98816 return $this;
98817 }
98818
98819 public function outdent()
98820 {
98821 $this->indentationLevel -= 1;
98822
98823 if ($this->indentationLevel < 0) {
98824 throw new RuntimeException('The identation level cannot be less than zero.');
98825 }
98826
98827 return $this;
98828 }
98829
98830 /**
98831 * @param string $content
98832 *
98833 * @return Writer
98834 */
98835 public function writeln($content)
98836 {
98837 $this->write($content."\n");
98838
98839 return $this;
98840 }
98841
98842 public function revert()
98843 {
98844 $change = array_pop($this->changes);
98845 $this->changeCount -=1 ;
98846 $this->content = substr($this->content, 0, -1 * strlen($change));
98847 }
98848
98849 /**
98850 * @param string $content
98851 *
98852 * @return Writer
98853 */
98854 public function write($content)
98855 {
98856 $addition = '';
98857
98858 $lines = explode("\n", $content);
98859 for ($i=0,$c=count($lines); $i<$c; $i++) {
98860 if ($this->indentationLevel > 0
98861 && !empty($lines[$i])
98862 && ((empty($addition) && "\n" === substr($this->content, -1)) || "\n" === substr($addition, -1))) {
98863 $addition .= str_repeat(' ', $this->indentationLevel * $this->indentationSpaces);
98864 }
98865
98866 $addition .= $lines[$i];
98867
98868 if ($i+1 < $c) {
98869 $addition .= "\n";
98870 }
98871 }
98872
98873 $this->content .= $addition;
98874 $this->changes[] = $addition;
98875 $this->changeCount += 1;
98876
98877 return $this;
98878 }
98879
98880 public function rtrim($preserveNewLines = true)
98881 {
98882 if (!$preserveNewLines) {
98883 $this->content = rtrim($this->content);
98884
98885 return $this;
98886 }
98887
98888 $addNl = "\n" === substr($this->content, -1);
98889 $this->content = rtrim($this->content);
98890
98891 if ($addNl) {
98892 $this->content .= "\n";
98893 }
98894
98895 return $this;
98896 }
98897
98898 public function reset()
98899 {
98900 $this->content = '';
98901 $this->indentationLevel = 0;
98902
98903 return $this;
98904 }
98905
98906 public function getContent()
98907 {
98908 return $this->content;
98909 }
98910 }
98911 <?php
98912
98913 /*
98914 * Copyright 2013 Johannes M. Schmitt <schmittjoh@gmail.com>
98915 *
98916 * Licensed under the Apache License, Version 2.0 (the "License");
98917 * you may not use this file except in compliance with the License.
98918 * You may obtain a copy of the License at
98919 *
98920 * http://www.apache.org/licenses/LICENSE-2.0
98921 *
98922 * Unless required by applicable law or agreed to in writing, software
98923 * distributed under the License is distributed on an "AS IS" BASIS,
98924 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
98925 * See the License for the specific language governing permissions and
98926 * limitations under the License.
98927 */
98928
98929 namespace JMS\Serializer\Twig;
98930
98931 use JMS\Serializer\SerializerInterface;
98932 use JMS\Serializer\SerializationContext;
98933
98934 /**
98935 * Serializer helper twig extension
98936 *
98937 * Basically provides access to JMSSerializer from Twig
98938 */
98939 class SerializerExtension extends \Twig_Extension
98940 {
98941 protected $serializer;
98942
98943 public function getName()
98944 {
98945 return 'jms_serializer';
98946 }
98947
98948 public function __construct(SerializerInterface $serializer)
98949 {
98950 $this->serializer = $serializer;
98951 }
98952
98953 public function getFilters()
98954 {
98955 return array(
98956 'serialize' => new \Twig_Filter_Method($this, 'serialize'),
98957 );
98958 }
98959
98960 public function getFunctions()
98961 {
98962 return array(
98963 'serialization_context' => new \Twig_Function_Method($this, 'createContext'),
98964 );
98965 }
98966
98967 /**
98968 * Creates the serialization context
98969 *
98970 * @return SerializationContext
98971 */
98972 public function createContext()
98973 {
98974 return SerializationContext::create();
98975 }
98976
98977 /**
98978 * @param object $object
98979 * @param string $type
98980 * @param SerializationContext $context
98981 */
98982 public function serialize($object, $type = 'json', SerializationContext $context = null)
98983 {
98984 return $this->serializer->serialize($object, $type, $context);
98985 }
98986 }
98987 <?php
98988
98989 /*
98990 * Copyright 2013 Johannes M. Schmitt <schmittjoh@gmail.com>
98991 *
98992 * Licensed under the Apache License, Version 2.0 (the "License");
98993 * you may not use this file except in compliance with the License.
98994 * You may obtain a copy of the License at
98995 *
98996 * http://www.apache.org/licenses/LICENSE-2.0
98997 *
98998 * Unless required by applicable law or agreed to in writing, software
98999 * distributed under the License is distributed on an "AS IS" BASIS,
99000 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
99001 * See the License for the specific language governing permissions and
99002 * limitations under the License.
99003 */
99004
99005 namespace JMS\Serializer;
99006
99007 /**
99008 * Parses a serializer type.
99009 *
99010 * @author Johannes M. Schmitt <schmittjoh@gmail.com>
99011 */
99012 final class TypeParser extends \JMS\Parser\AbstractParser
99013 {
99014 const T_NAME = 1;
99015 const T_STRING = 2;
99016 const T_OPEN_BRACKET = 3;
99017 const T_CLOSE_BRACKET = 4;
99018 const T_COMMA = 5;
99019 const T_NONE = 6;
99020
99021 public function __construct()
99022 {
99023 parent::__construct(new \JMS\Parser\SimpleLexer(
99024 '/
99025 # PHP Class Names
99026 ((?:[a-zA-Z_\x7f-\xff][a-zA-Z0-9_\x7f-\xff]*\\\\)*[a-zA-Z_\x7f-\xff][a-zA-Z0-9_\x7f-\xff]*)
99027
99028 # Strings
99029 |("(?:[^"]|"")*"|\'(?:[^\']|\'\')*\')
99030
99031 # Ignore whitespace
99032 |\s*
99033
99034 # Terminals
99035 |(.)
99036 /x',
99037 array(self::T_NAME => 'T_NAME', self::T_STRING => 'T_STRING', self::T_OPEN_BRACKET => 'T_OPEN_BRACKET',
99038 self::T_CLOSE_BRACKET => 'T_CLOSE_BRACKET', self::T_COMMA => 'T_COMMA', self::T_NONE => 'T_NONE'),
99039 function($value) {
99040 switch ($value[0]) {
99041 case '"':
99042 case "'":
99043 return array(TypeParser::T_STRING, substr($value, 1, -1));
99044
99045 case '<':
99046 return array(TypeParser::T_OPEN_BRACKET, '<');
99047
99048 case '>':
99049 return array(TypeParser::T_CLOSE_BRACKET, '>');
99050
99051 case ',':
99052 return array(TypeParser::T_COMMA, ',');
99053
99054 default:
99055 if (preg_match('/^(?:[a-zA-Z_\x7f-\xff][a-zA-Z0-9_\x7f-\xff]*\\\\)*[a-zA-Z_\x7f-\xff][a-zA-Z0-9_\x7f-\xff]*$/', $value)) {
99056 return array(TypeParser::T_NAME, $value);
99057 }
99058
99059 return array(TypeParser::T_NONE, $value);
99060 }
99061 }
99062 ));
99063 }
99064
99065 /**
99066 * @return array of the format ["name" => string, "params" => array]
99067 */
99068 protected function parseInternal()
99069 {
99070 $typeName = $this->match(self::T_NAME);
99071 if ( ! $this->lexer->isNext(self::T_OPEN_BRACKET)) {
99072 return array('name' => $typeName, 'params' => array());
99073 }
99074
99075 $this->match(self::T_OPEN_BRACKET);
99076 $params = array();
99077 do {
99078 if ($this->lexer->isNext(self::T_NAME)) {
99079 $params[] = $this->parseInternal();
99080 } else if ($this->lexer->isNext(self::T_STRING)) {
99081 $params[] = $this->match(self::T_STRING);
99082 } else {
99083 $this->matchAny(array(self::T_NAME, self::T_STRING)); // Will throw an exception.
99084 }
99085 } while ($this->lexer->isNext(self::T_COMMA) && $this->lexer->moveNext());
99086
99087 $this->match(self::T_CLOSE_BRACKET);
99088
99089 return array('name' => $typeName, 'params' => $params);
99090 }
99091 }
99092 <?php
99093
99094 /*
99095 * Copyright 2013 Johannes M. Schmitt <schmittjoh@gmail.com>
99096 *
99097 * Licensed under the Apache License, Version 2.0 (the "License");
99098 * you may not use this file except in compliance with the License.
99099 * You may obtain a copy of the License at
99100 *
99101 * http://www.apache.org/licenses/LICENSE-2.0
99102 *
99103 * Unless required by applicable law or agreed to in writing, software
99104 * distributed under the License is distributed on an "AS IS" BASIS,
99105 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
99106 * See the License for the specific language governing permissions and
99107 * limitations under the License.
99108 */
99109
99110 namespace JMS\Serializer\Exclusion;
99111
99112 use JMS\Serializer\Metadata\ClassMetadata;
99113 use JMS\Serializer\Metadata\PropertyMetadata;
99114 use JMS\Serializer\Context;
99115
99116 /**
99117 * Interface for exclusion strategies.
99118 *
99119 * @author Johannes M. Schmitt <schmittjoh@gmail.com>
99120 */
99121 interface ExclusionStrategyInterface
99122 {
99123 /**
99124 * Whether the class should be skipped.
99125 *
99126 * @param ClassMetadata $metadata
99127 * @param Context $navigatorContext
99128 *
99129 * @return boolean
99130 */
99131 public function shouldSkipClass(ClassMetadata $metadata, Context $context);
99132
99133 /**
99134 * Whether the property should be skipped.
99135 *
99136 * @param PropertyMetadata $property
99137 * @param Context $navigatorContext
99138 *
99139 * @return boolean
99140 */
99141 public function shouldSkipProperty(PropertyMetadata $property, Context $context);
99142 }
99143 <?php
99144
99145 /*
99146 * Copyright 2013 Johannes M. Schmitt <schmittjoh@gmail.com>
99147 *
99148 * Licensed under the Apache License, Version 2.0 (the "License");
99149 * you may not use this file except in compliance with the License.
99150 * You may obtain a copy of the License at
99151 *
99152 * http://www.apache.org/licenses/LICENSE-2.0
99153 *
99154 * Unless required by applicable law or agreed to in writing, software
99155 * distributed under the License is distributed on an "AS IS" BASIS,
99156 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
99157 * See the License for the specific language governing permissions and
99158 * limitations under the License.
99159 */
99160
99161 namespace JMS\Serializer\Exclusion;
99162
99163 use JMS\Serializer\Metadata\ClassMetadata;
99164 use JMS\Serializer\Metadata\PropertyMetadata;
99165 use JMS\Serializer\Context;
99166
99167 class GroupsExclusionStrategy implements ExclusionStrategyInterface
99168 {
99169 const DEFAULT_GROUP = 'Default';
99170
99171 private $groups = array();
99172
99173 public function __construct(array $groups)
99174 {
99175 if (empty($groups)) {
99176 $groups = array(self::DEFAULT_GROUP);
99177 }
99178
99179 foreach ($groups as $group) {
99180 $this->groups[$group] = true;
99181 }
99182 }
99183
99184 /**
99185 * {@inheritDoc}
99186 */
99187 public function shouldSkipClass(ClassMetadata $metadata, Context $navigatorContext)
99188 {
99189 return false;
99190 }
99191
99192 /**
99193 * {@inheritDoc}
99194 */
99195 public function shouldSkipProperty(PropertyMetadata $property, Context $navigatorContext)
99196 {
99197 if ( ! $property->groups) {
99198 return ! isset($this->groups[self::DEFAULT_GROUP]);
99199 }
99200
99201 foreach ($property->groups as $group) {
99202 if (isset($this->groups[$group])) {
99203 return false;
99204 }
99205 }
99206
99207 return true;
99208 }
99209 }
99210 <?php
99211
99212 /*
99213 * Copyright 2011 Johannes M. Schmitt <schmittjoh@gmail.com>
99214 *
99215 * Licensed under the Apache License, Version 2.0 (the "License");
99216 * you may not use this file except in compliance with the License.
99217 * You may obtain a copy of the License at
99218 *
99219 * http://www.apache.org/licenses/LICENSE-2.0
99220 *
99221 * Unless required by applicable law or agreed to in writing, software
99222 * distributed under the License is distributed on an "AS IS" BASIS,
99223 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
99224 * See the License for the specific language governing permissions and
99225 * limitations under the License.
99226 */
99227
99228 namespace JMS\Serializer\Exclusion;
99229
99230 use JMS\Serializer\Context;
99231 use JMS\Serializer\Metadata\ClassMetadata;
99232 use JMS\Serializer\Metadata\PropertyMetadata;
99233
99234 /**
99235 * @author Adrien Brault <adrien.brault@gmail.com>
99236 */
99237 class DepthExclusionStrategy implements ExclusionStrategyInterface
99238 {
99239 /**
99240 * {@inheritDoc}
99241 */
99242 public function shouldSkipClass(ClassMetadata $metadata, Context $context)
99243 {
99244 return $this->isTooDeep($context);
99245 }
99246
99247 /**
99248 * {@inheritDoc}
99249 */
99250 public function shouldSkipProperty(PropertyMetadata $property, Context $context)
99251 {
99252 return $this->isTooDeep($context);
99253 }
99254
99255 private function isTooDeep(Context $context)
99256 {
99257 $depth = $context->getDepth();
99258 $metadataStack = $context->getMetadataStack();
99259
99260 $nthProperty = 0;
99261 // iterate from the first added items to the lasts
99262 for ($i = $metadataStack->count() - 1; $i > 0; $i--) {
99263 $metadata = $metadataStack[$i];
99264 if ($metadata instanceof PropertyMetadata) {
99265 $nthProperty++;
99266 $relativeDepth = $depth - $nthProperty;
99267
99268 if (null !== $metadata->maxDepth && $relativeDepth > $metadata->maxDepth) {
99269 return true;
99270 }
99271 }
99272 }
99273
99274 return false;
99275 }
99276 }
99277 <?php
99278
99279 /*
99280 * Copyright 2013 Johannes M. Schmitt <schmittjoh@gmail.com>
99281 *
99282 * Licensed under the Apache License, Version 2.0 (the "License");
99283 * you may not use this file except in compliance with the License.
99284 * You may obtain a copy of the License at
99285 *
99286 * http://www.apache.org/licenses/LICENSE-2.0
99287 *
99288 * Unless required by applicable law or agreed to in writing, software
99289 * distributed under the License is distributed on an "AS IS" BASIS,
99290 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
99291 * See the License for the specific language governing permissions and
99292 * limitations under the License.
99293 */
99294
99295 namespace JMS\Serializer\Exclusion;
99296
99297 use JMS\Serializer\Context;
99298 use JMS\Serializer\Metadata\ClassMetadata;
99299 use JMS\Serializer\Metadata\PropertyMetadata;
99300 use PhpCollection\Sequence;
99301 use PhpCollection\SequenceInterface;
99302
99303 /**
99304 * Disjunct Exclusion Strategy.
99305 *
99306 * This strategy is short-circuiting and will skip a class, or property as soon as one of the delegates skips it.
99307 *
99308 * @author Johannes M. Schmitt <schmittjoh@gmail.com>
99309 */
99310 class DisjunctExclusionStrategy implements ExclusionStrategyInterface
99311 {
99312 /** @var \PhpCollection\SequenceInterface */
99313 private $delegates;
99314
99315 /**
99316 * @param ExclusionStrategyInterface[]|SequenceInterface $delegates
99317 */
99318 public function __construct($delegates)
99319 {
99320 if ( ! $delegates instanceof SequenceInterface) {
99321 $delegates = new Sequence($delegates);
99322 }
99323
99324 $this->delegates = $delegates;
99325 }
99326
99327 public function addStrategy(ExclusionStrategyInterface $strategy)
99328 {
99329 $this->delegates->add($strategy);
99330 }
99331
99332 /**
99333 * Whether the class should be skipped.
99334 *
99335 * @param ClassMetadata $metadata
99336 * @param Context $navigatorContext
99337 *
99338 * @return boolean
99339 */
99340 public function shouldSkipClass(ClassMetadata $metadata, Context $context)
99341 {
99342 foreach ($this->delegates as $delegate) {
99343 /** @var $delegate ExclusionStrategyInterface */
99344 if ($delegate->shouldSkipClass($metadata, $context)) {
99345 return true;
99346 }
99347 }
99348
99349 return false;
99350 }
99351
99352 /**
99353 * Whether the property should be skipped.
99354 *
99355 * @param PropertyMetadata $property
99356 * @param Context $navigatorContext
99357 *
99358 * @return boolean
99359 */
99360 public function shouldSkipProperty(PropertyMetadata $property, Context $context)
99361 {
99362 foreach ($this->delegates as $delegate) {
99363 /** @var $delegate ExclusionStrategyInterface */
99364 if ($delegate->shouldSkipProperty($property, $context)) {
99365 return true;
99366 }
99367 }
99368
99369 return false;
99370 }
99371 }<?php
99372
99373 /*
99374 * Copyright 2013 Johannes M. Schmitt <schmittjoh@gmail.com>
99375 *
99376 * Licensed under the Apache License, Version 2.0 (the "License");
99377 * you may not use this file except in compliance with the License.
99378 * You may obtain a copy of the License at
99379 *
99380 * http://www.apache.org/licenses/LICENSE-2.0
99381 *
99382 * Unless required by applicable law or agreed to in writing, software
99383 * distributed under the License is distributed on an "AS IS" BASIS,
99384 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
99385 * See the License for the specific language governing permissions and
99386 * limitations under the License.
99387 */
99388
99389 namespace JMS\Serializer\Exclusion;
99390
99391 use JMS\Serializer\Metadata\ClassMetadata;
99392 use JMS\Serializer\Metadata\PropertyMetadata;
99393 use JMS\Serializer\Context;
99394
99395 class VersionExclusionStrategy implements ExclusionStrategyInterface
99396 {
99397 private $version;
99398
99399 public function __construct($version)
99400 {
99401 $this->version = $version;
99402 }
99403
99404 /**
99405 * {@inheritDoc}
99406 */
99407 public function shouldSkipClass(ClassMetadata $metadata, Context $navigatorContext)
99408 {
99409 return false;
99410 }
99411
99412 /**
99413 * {@inheritDoc}
99414 */
99415 public function shouldSkipProperty(PropertyMetadata $property, Context $navigatorContext)
99416 {
99417 if ((null !== $version = $property->sinceVersion) && version_compare($this->version, $version, '<')) {
99418 return true;
99419 }
99420
99421 if ((null !== $version = $property->untilVersion) && version_compare($this->version, $version, '>')) {
99422 return true;
99423 }
99424
99425 return false;
99426 }
99427 }
99428 <?php
99429
99430 /*
99431 * Copyright 2013 Johannes M. Schmitt <schmittjoh@gmail.com>
99432 *
99433 * Licensed under the Apache License, Version 2.0 (the "License");
99434 * you may not use this file except in compliance with the License.
99435 * You may obtain a copy of the License at
99436 *
99437 * http://www.apache.org/licenses/LICENSE-2.0
99438 *
99439 * Unless required by applicable law or agreed to in writing, software
99440 * distributed under the License is distributed on an "AS IS" BASIS,
99441 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
99442 * See the License for the specific language governing permissions and
99443 * limitations under the License.
99444 */
99445
99446 namespace JMS\Serializer;
99447
99448 use JMS\Serializer\Metadata\ClassMetadata;
99449 use JMS\Serializer\Metadata\PropertyMetadata;
99450
99451 /**
99452 * Interface for visitors.
99453 *
99454 * This contains the minimal set of values that must be supported for any
99455 * output format.
99456 *
99457 * @author Johannes M. Schmitt <schmittjoh@gmail.com>
99458 */
99459 interface VisitorInterface
99460 {
99461 /**
99462 * Allows visitors to convert the input data to a different representation
99463 * before the actual serialization/deserialization process starts.
99464 *
99465 * @param mixed $data
99466 *
99467 * @return mixed
99468 */
99469 public function prepare($data);
99470
99471 /**
99472 * @param mixed $data
99473 * @param array $type
99474 *
99475 * @return mixed
99476 */
99477 public function visitNull($data, array $type, Context $context);
99478
99479 /**
99480 * @param mixed $data
99481 * @param array $type
99482 *
99483 * @return mixed
99484 */
99485 public function visitString($data, array $type, Context $context);
99486
99487 /**
99488 * @param mixed $data
99489 * @param array $type
99490 *
99491 * @return mixed
99492 */
99493 public function visitBoolean($data, array $type, Context $context);
99494
99495 /**
99496 * @param mixed $data
99497 * @param array $type
99498 *
99499 * @return mixed
99500 */
99501 public function visitDouble($data, array $type, Context $context);
99502
99503 /**
99504 * @param mixed $data
99505 * @param array $type
99506 *
99507 * @return mixed
99508 */
99509 public function visitInteger($data, array $type, Context $context);
99510
99511 /**
99512 * @param mixed $data
99513 * @param array $type
99514 *
99515 * @return mixed
99516 */
99517 public function visitArray($data, array $type, Context $context);
99518
99519 /**
99520 * Called before the properties of the object are being visited.
99521 *
99522 * @param ClassMetadata $metadata
99523 * @param mixed $data
99524 * @param array $type
99525 *
99526 * @return void
99527 */
99528 public function startVisitingObject(ClassMetadata $metadata, $data, array $type, Context $context);
99529
99530 /**
99531 * @param PropertyMetadata $metadata
99532 * @param mixed $data
99533 *
99534 * @return void
99535 */
99536 public function visitProperty(PropertyMetadata $metadata, $data, Context $context);
99537
99538 /**
99539 * Called after all properties of the object have been visited.
99540 *
99541 * @param ClassMetadata $metadata
99542 * @param mixed $data
99543 * @param array $type
99544 *
99545 * @return mixed
99546 */
99547 public function endVisitingObject(ClassMetadata $metadata, $data, array $type, Context $context);
99548
99549 /**
99550 * Called before serialization/deserialization starts.
99551 *
99552 * @param GraphNavigator $navigator
99553 *
99554 * @return void
99555 */
99556 public function setNavigator(GraphNavigator $navigator);
99557
99558 /**
99559 * @deprecated use Context::getNavigator/Context::accept instead
99560 * @return GraphNavigator
99561 */
99562 public function getNavigator();
99563
99564 /**
99565 * @return object|array|scalar
99566 */
99567 public function getResult();
99568 }
99569 <?php
99570
99571 /*
99572 * Copyright 2013 Johannes M. Schmitt <schmittjoh@gmail.com>
99573 *
99574 * Licensed under the Apache License, Version 2.0 (the "License");
99575 * you may not use this file except in compliance with the License.
99576 * You may obtain a copy of the License at
99577 *
99578 * http://www.apache.org/licenses/LICENSE-2.0
99579 *
99580 * Unless required by applicable law or agreed to in writing, software
99581 * distributed under the License is distributed on an "AS IS" BASIS,
99582 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
99583 * See the License for the specific language governing permissions and
99584 * limitations under the License.
99585 */
99586
99587 namespace JMS\Serializer;
99588
99589 use Symfony\Component\Yaml\Inline;
99590 use JMS\Serializer\Metadata\PropertyMetadata;
99591 use JMS\Serializer\Naming\PropertyNamingStrategyInterface;
99592 use JMS\Serializer\Metadata\ClassMetadata;
99593 use JMS\Serializer\Util\Writer;
99594
99595 /**
99596 * Serialization Visitor for the YAML format.
99597 *
99598 * @see http://www.yaml.org/spec/
99599 * @author Johannes M. Schmitt <schmittjoh@gmail.com>
99600 */
99601 class YamlSerializationVisitor extends AbstractVisitor
99602 {
99603 public $writer;
99604
99605 private $navigator;
99606 private $stack;
99607 private $metadataStack;
99608 private $currentMetadata;
99609
99610 public function __construct(PropertyNamingStrategyInterface $namingStrategy)
99611 {
99612 parent::__construct($namingStrategy);
99613
99614 $this->writer = new Writer();
99615 }
99616
99617 public function setNavigator(GraphNavigator $navigator)
99618 {
99619 $this->navigator = $navigator;
99620 $this->writer->reset();
99621 $this->stack = new \SplStack;
99622 $this->metadataStack = new \SplStack;
99623 }
99624
99625 public function visitNull($data, array $type, Context $context)
99626 {
99627 if ('' === $this->writer->content) {
99628 $this->writer->writeln('null');
99629 }
99630
99631 return 'null';
99632 }
99633
99634 public function visitString($data, array $type, Context $context)
99635 {
99636 $v = Inline::dump($data);
99637
99638 if ('' === $this->writer->content) {
99639 $this->writer->writeln($v);
99640 }
99641
99642 return $v;
99643 }
99644
99645 /**
99646 * @param array $data
99647 * @param array $type
99648 */
99649 public function visitArray($data, array $type, Context $context)
99650 {
99651 $count = $this->writer->changeCount;
99652 $isList = array_keys($data) === range(0, count($data) - 1);
99653
99654 foreach ($data as $k => $v) {
99655 if (null === $v && (!is_string($k) || ! $context->shouldSerializeNull())) {
99656 continue;
99657 }
99658
99659 if ($isList) {
99660 $this->writer->writeln('-');
99661 } else {
99662 $this->writer->writeln(Inline::dump($k).':');
99663 }
99664
99665 $this->writer->indent();
99666
99667 if (null !== $v = $this->navigator->accept($v, null, $context)) {
99668 $this->writer
99669 ->rtrim(false)
99670 ->writeln(' '.$v)
99671 ;
99672 }
99673
99674 $this->writer->outdent();
99675 }
99676
99677 if ($count === $this->writer->changeCount && isset($type['params'][1])) {
99678 $this->writer
99679 ->rtrim(false)
99680 ->writeln(' {}')
99681 ;
99682 }
99683 }
99684
99685 public function visitBoolean($data, array $type, Context $context)
99686 {
99687 $v = $data ? 'true' : 'false';
99688
99689 if ('' === $this->writer->content) {
99690 $this->writer->writeln($v);
99691 }
99692
99693 return $v;
99694 }
99695
99696 public function visitDouble($data, array $type, Context $context)
99697 {
99698 $v = (string) $data;
99699
99700 if ('' === $this->writer->content) {
99701 $this->writer->writeln($v);
99702 }
99703
99704 return $v;
99705 }
99706
99707 public function visitInteger($data, array $type, Context $context)
99708 {
99709 $v = (string) $data;
99710
99711 if ('' === $this->writer->content) {
99712 $this->writer->writeln($v);
99713 }
99714
99715 return $v;
99716 }
99717
99718 public function startVisitingObject(ClassMetadata $metadata, $data, array $type, Context $context)
99719 {
99720 }
99721
99722 public function visitProperty(PropertyMetadata $metadata, $data, Context $context)
99723 {
99724 $v = $metadata->getValue($data);
99725
99726 if (null === $v && !$context->shouldSerializeNull()) {
99727 return;
99728 }
99729
99730 $name = $this->namingStrategy->translateName($metadata);
99731
99732 if (!$metadata->inline) {
99733 $this->writer
99734 ->writeln(Inline::dump($name).':')
99735 ->indent();
99736 }
99737
99738 $this->setCurrentMetadata($metadata);
99739
99740 $count = $this->writer->changeCount;
99741
99742 if (null !== $v = $this->navigator->accept($v, $metadata->type, $context)) {
99743 $this->writer
99744 ->rtrim(false)
99745 ->writeln(' '.$v)
99746 ;
99747 } elseif ($count === $this->writer->changeCount) {
99748 $this->writer->revert();
99749 }
99750
99751 if (!$metadata->inline) {
99752 $this->writer->outdent();
99753 }
99754 $this->revertCurrentMetadata();
99755 }
99756
99757 public function endVisitingObject(ClassMetadata $metadata, $data, array $type, Context $context)
99758 {
99759 }
99760
99761 public function setCurrentMetadata(PropertyMetadata $metadata)
99762 {
99763 $this->metadataStack->push($this->currentMetadata);
99764 $this->currentMetadata = $metadata;
99765 }
99766
99767 public function revertCurrentMetadata()
99768 {
99769 return $this->currentMetadata = $this->metadataStack->pop();
99770 }
99771
99772 public function getNavigator()
99773 {
99774 return $this->navigator;
99775 }
99776
99777 public function getResult()
99778 {
99779 return $this->writer->getContent();
99780 }
99781 }
99782 <?php
99783
99784 /*
99785 * Copyright 2013 Johannes M. Schmitt <schmittjoh@gmail.com>
99786 *
99787 * Licensed under the Apache License, Version 2.0 (the "License");
99788 * you may not use this file except in compliance with the License.
99789 * You may obtain a copy of the License at
99790 *
99791 * http://www.apache.org/licenses/LICENSE-2.0
99792 *
99793 * Unless required by applicable law or agreed to in writing, software
99794 * distributed under the License is distributed on an "AS IS" BASIS,
99795 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
99796 * See the License for the specific language governing permissions and
99797 * limitations under the License.
99798 */
99799
99800 namespace JMS\Serializer\Naming;
99801
99802 use JMS\Serializer\Metadata\PropertyMetadata;
99803
99804 class CacheNamingStrategy implements PropertyNamingStrategyInterface
99805 {
99806 private $delegate;
99807 private $cache;
99808
99809 public function __construct(PropertyNamingStrategyInterface $delegate)
99810 {
99811 $this->delegate = $delegate;
99812 $this->cache = new \SplObjectStorage();
99813 }
99814
99815 public function translateName(PropertyMetadata $property)
99816 {
99817 if (isset($this->cache[$property])) {
99818 return $this->cache[$property];
99819 }
99820
99821 return $this->cache[$property] = $this->delegate->translateName($property);
99822 }
99823 }
99824 <?php
99825
99826 /*
99827 * Copyright 2013 Johannes M. Schmitt <schmittjoh@gmail.com>
99828 *
99829 * Licensed under the Apache License, Version 2.0 (the "License");
99830 * you may not use this file except in compliance with the License.
99831 * You may obtain a copy of the License at
99832 *
99833 * http://www.apache.org/licenses/LICENSE-2.0
99834 *
99835 * Unless required by applicable law or agreed to in writing, software
99836 * distributed under the License is distributed on an "AS IS" BASIS,
99837 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
99838 * See the License for the specific language governing permissions and
99839 * limitations under the License.
99840 */
99841
99842 namespace JMS\Serializer\Naming;
99843
99844 use JMS\Serializer\Metadata\PropertyMetadata;
99845
99846 /**
99847 * Generic naming strategy which translates a camel-cased property name.
99848 *
99849 * @author Johannes M. Schmitt <schmittjoh@gmail.com>
99850 */
99851 class CamelCaseNamingStrategy implements PropertyNamingStrategyInterface
99852 {
99853 private $separator;
99854 private $lowerCase;
99855
99856 public function __construct($separator = '_', $lowerCase = true)
99857 {
99858 $this->separator = $separator;
99859 $this->lowerCase = $lowerCase;
99860 }
99861
99862 /**
99863 * {@inheritDoc}
99864 */
99865 public function translateName(PropertyMetadata $property)
99866 {
99867 $name = preg_replace('/[A-Z]/', $this->separator.'\\0', $property->name);
99868
99869 if ($this->lowerCase) {
99870 return strtolower($name);
99871 }
99872
99873 return ucfirst($name);
99874 }
99875 }
99876 <?php
99877
99878 /*
99879 * Copyright 2013 Johannes M. Schmitt <schmittjoh@gmail.com>
99880 *
99881 * Licensed under the Apache License, Version 2.0 (the "License");
99882 * you may not use this file except in compliance with the License.
99883 * You may obtain a copy of the License at
99884 *
99885 * http://www.apache.org/licenses/LICENSE-2.0
99886 *
99887 * Unless required by applicable law or agreed to in writing, software
99888 * distributed under the License is distributed on an "AS IS" BASIS,
99889 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
99890 * See the License for the specific language governing permissions and
99891 * limitations under the License.
99892 */
99893
99894 namespace JMS\Serializer\Naming;
99895
99896 use JMS\Serializer\Metadata\PropertyMetadata;
99897
99898 /**
99899 * Interface for property naming strategies.
99900 *
99901 * Implementations translate the property name to a serialized name that is
99902 * displayed.
99903 *
99904 * @author Johannes M. Schmitt <schmittjoh@gmail.com>
99905 */
99906 interface PropertyNamingStrategyInterface
99907 {
99908 /**
99909 * Translates the name of the property to the serialized version.
99910 *
99911 * @param PropertyMetadata $property
99912 *
99913 * @return string
99914 */
99915 public function translateName(PropertyMetadata $property);
99916 }
99917 <?php
99918
99919 /*
99920 * Copyright 2013 Johannes M. Schmitt <schmittjoh@gmail.com>
99921 *
99922 * Licensed under the Apache License, Version 2.0 (the "License");
99923 * you may not use this file except in compliance with the License.
99924 * You may obtain a copy of the License at
99925 *
99926 * http://www.apache.org/licenses/LICENSE-2.0
99927 *
99928 * Unless required by applicable law or agreed to in writing, software
99929 * distributed under the License is distributed on an "AS IS" BASIS,
99930 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
99931 * See the License for the specific language governing permissions and
99932 * limitations under the License.
99933 */
99934
99935 namespace JMS\Serializer\Naming;
99936
99937 use JMS\Serializer\Metadata\PropertyMetadata;
99938
99939 /**
99940 * Naming strategy which uses an annotation to translate the property name.
99941 *
99942 * @author Johannes M. Schmitt <schmittjoh@gmail.com>
99943 */
99944 class SerializedNameAnnotationStrategy implements PropertyNamingStrategyInterface
99945 {
99946 private $delegate;
99947
99948 public function __construct(PropertyNamingStrategyInterface $namingStrategy)
99949 {
99950 $this->delegate = $namingStrategy;
99951 }
99952
99953 /**
99954 * {@inheritDoc}
99955 */
99956 public function translateName(PropertyMetadata $property)
99957 {
99958 if (null !== $name = $property->serializedName) {
99959 return $name;
99960 }
99961
99962 return $this->delegate->translateName($property);
99963 }
99964 }
99965 <?php
99966
99967 /*
99968 * Copyright 2013 Johannes M. Schmitt <schmittjoh@gmail.com>
99969 *
99970 * Licensed under the Apache License, Version 2.0 (the "License");
99971 * you may not use this file except in compliance with the License.
99972 * You may obtain a copy of the License at
99973 *
99974 * http://www.apache.org/licenses/LICENSE-2.0
99975 *
99976 * Unless required by applicable law or agreed to in writing, software
99977 * distributed under the License is distributed on an "AS IS" BASIS,
99978 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
99979 * See the License for the specific language governing permissions and
99980 * limitations under the License.
99981 */
99982
99983 namespace JMS\Serializer\Naming;
99984
99985 use JMS\Serializer\Naming\PropertyNamingStrategyInterface;
99986 use JMS\Serializer\Metadata\PropertyMetadata;
99987
99988 class IdenticalPropertyNamingStrategy implements PropertyNamingStrategyInterface
99989 {
99990 public function translateName(PropertyMetadata $property)
99991 {
99992 return $property->name;
99993 }
99994 }
99995 <?php
99996
99997 /*
99998 * Copyright 2013 Johannes M. Schmitt <schmittjoh@gmail.com>
99999 *
100000 * Licensed under the Apache License, Version 2.0 (the "License");
100001 * you may not use this file except in compliance with the License.
100002 * You may obtain a copy of the License at
100003 *
100004 * http://www.apache.org/licenses/LICENSE-2.0
100005 *
100006 * Unless required by applicable law or agreed to in writing, software
100007 * distributed under the License is distributed on an "AS IS" BASIS,
100008 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
100009 * See the License for the specific language governing permissions and
100010 * limitations under the License.
100011 */
100012
100013 namespace JMS\Serializer\Annotation;
100014
100015 /**
100016 * @Annotation
100017 * @Target({"PROPERTY", "METHOD"})
100018 */
100019 final class XmlAttributeMap
100020 {
100021 }
100022 <?php
100023
100024 /*
100025 * Copyright 2013 Johannes M. Schmitt <schmittjoh@gmail.com>
100026 *
100027 * Licensed under the Apache License, Version 2.0 (the "License");
100028 * you may not use this file except in compliance with the License.
100029 * You may obtain a copy of the License at
100030 *
100031 * http://www.apache.org/licenses/LICENSE-2.0
100032 *
100033 * Unless required by applicable law or agreed to in writing, software
100034 * distributed under the License is distributed on an "AS IS" BASIS,
100035 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
100036 * See the License for the specific language governing permissions and
100037 * limitations under the License.
100038 */
100039
100040 namespace JMS\Serializer\Annotation;
100041
100042 /**
100043 * @Annotation
100044 * @Target({"PROPERTY","METHOD"})
100045 */
100046 final class XmlMap extends XmlCollection
100047 {
100048 /**
100049 * @var string
100050 */
100051 public $keyAttribute = '_key';
100052 }
100053 <?php
100054
100055 /*
100056 * Copyright 2013 Johannes M. Schmitt <schmittjoh@gmail.com>
100057 *
100058 * Licensed under the Apache License, Version 2.0 (the "License");
100059 * you may not use this file except in compliance with the License.
100060 * You may obtain a copy of the License at
100061 *
100062 * http://www.apache.org/licenses/LICENSE-2.0
100063 *
100064 * Unless required by applicable law or agreed to in writing, software
100065 * distributed under the License is distributed on an "AS IS" BASIS,
100066 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
100067 * See the License for the specific language governing permissions and
100068 * limitations under the License.
100069 */
100070
100071 namespace JMS\Serializer\Annotation;
100072
100073 use JMS\Serializer\Exception\RuntimeException;
100074
100075 /**
100076 * @Annotation
100077 * @Target({"PROPERTY","METHOD"})
100078 */
100079 final class SerializedName
100080 {
100081 public $name;
100082
100083 public function __construct(array $values)
100084 {
100085 if (!is_string($values['value'])) {
100086 throw new RuntimeException(sprintf('"value" must be a string.'));
100087 }
100088
100089 $this->name = $values['value'];
100090 }
100091 }
100092 <?php
100093
100094 /*
100095 * Copyright 2013 Johannes M. Schmitt <schmittjoh@gmail.com>
100096 *
100097 * Licensed under the Apache License, Version 2.0 (the "License");
100098 * you may not use this file except in compliance with the License.
100099 * You may obtain a copy of the License at
100100 *
100101 * http://www.apache.org/licenses/LICENSE-2.0
100102 *
100103 * Unless required by applicable law or agreed to in writing, software
100104 * distributed under the License is distributed on an "AS IS" BASIS,
100105 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
100106 * See the License for the specific language governing permissions and
100107 * limitations under the License.
100108 */
100109
100110 namespace JMS\Serializer\Annotation;
100111
100112 /**
100113 * This annotation can be declared on methods which should be called
100114 * before the Serialization process.
100115 *
100116 * These methods do not need to be public, and should do any clean-up, or
100117 * preparation of the object that is necessary.
100118 *
100119 * @Annotation
100120 * @Target("METHOD")
100121 * @author Johannes M. Schmitt <schmittjoh@gmail.com>
100122 */
100123 final class PreSerialize
100124 {
100125 }
100126 <?php
100127
100128 /*
100129 * Copyright 2013 Johannes M. Schmitt <schmittjoh@gmail.com>
100130 *
100131 * Licensed under the Apache License, Version 2.0 (the "License");
100132 * you may not use this file except in compliance with the License.
100133 * You may obtain a copy of the License at
100134 *
100135 * http://www.apache.org/licenses/LICENSE-2.0
100136 *
100137 * Unless required by applicable law or agreed to in writing, software
100138 * distributed under the License is distributed on an "AS IS" BASIS,
100139 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
100140 * See the License for the specific language governing permissions and
100141 * limitations under the License.
100142 */
100143
100144 namespace JMS\Serializer\Annotation;
100145
100146 /**
100147 * @Annotation
100148 * @Target({"PROPERTY", "METHOD"})
100149 */
100150 final class XmlAttribute
100151 {
100152 }
100153 <?php
100154
100155 /*
100156 * Copyright 2013 Johannes M. Schmitt <schmittjoh@gmail.com>
100157 *
100158 * Licensed under the Apache License, Version 2.0 (the "License");
100159 * you may not use this file except in compliance with the License.
100160 * You may obtain a copy of the License at
100161 *
100162 * http://www.apache.org/licenses/LICENSE-2.0
100163 *
100164 * Unless required by applicable law or agreed to in writing, software
100165 * distributed under the License is distributed on an "AS IS" BASIS,
100166 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
100167 * See the License for the specific language governing permissions and
100168 * limitations under the License.
100169 */
100170
100171 namespace JMS\Serializer\Annotation;
100172
100173 abstract class Version
100174 {
100175 /**
100176 * @Required
100177 * @var string
100178 */
100179 public $version;
100180 }
100181 <?php
100182
100183 /*
100184 * Copyright 2013 Johannes M. Schmitt <schmittjoh@gmail.com>
100185 *
100186 * Licensed under the Apache License, Version 2.0 (the "License");
100187 * you may not use this file except in compliance with the License.
100188 * You may obtain a copy of the License at
100189 *
100190 * http://www.apache.org/licenses/LICENSE-2.0
100191 *
100192 * Unless required by applicable law or agreed to in writing, software
100193 * distributed under the License is distributed on an "AS IS" BASIS,
100194 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
100195 * See the License for the specific language governing permissions and
100196 * limitations under the License.
100197 */
100198
100199 namespace JMS\Serializer\Annotation;
100200
100201 abstract class XmlCollection
100202 {
100203 /**
100204 * @var string
100205 */
100206 public $entry = 'entry';
100207
100208 /**
100209 * @var boolean
100210 */
100211 public $inline = false;
100212 }
100213 <?php
100214
100215 /*
100216 * Copyright 2013 Johannes M. Schmitt <schmittjoh@gmail.com>
100217 *
100218 * Licensed under the Apache License, Version 2.0 (the "License");
100219 * you may not use this file except in compliance with the License.
100220 * You may obtain a copy of the License at
100221 *
100222 * http://www.apache.org/licenses/LICENSE-2.0
100223 *
100224 * Unless required by applicable law or agreed to in writing, software
100225 * distributed under the License is distributed on an "AS IS" BASIS,
100226 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
100227 * See the License for the specific language governing permissions and
100228 * limitations under the License.
100229 */
100230
100231 namespace JMS\Serializer\Annotation;
100232
100233 /**
100234 * @Annotation
100235 * @Target({"CLASS", "PROPERTY"})
100236 *
100237 * @author Johannes M. Schmitt <schmittjoh@gmail.com>
100238 */
100239 final class AccessType
100240 {
100241 /**
100242 * @Required
100243 * @var string
100244 */
100245 public $type;
100246 }
100247 <?php
100248
100249 /*
100250 * Copyright 2013 Johannes M. Schmitt <schmittjoh@gmail.com>
100251 *
100252 * Licensed under the Apache License, Version 2.0 (the "License");
100253 * you may not use this file except in compliance with the License.
100254 * You may obtain a copy of the License at
100255 *
100256 * http://www.apache.org/licenses/LICENSE-2.0
100257 *
100258 * Unless required by applicable law or agreed to in writing, software
100259 * distributed under the License is distributed on an "AS IS" BASIS,
100260 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
100261 * See the License for the specific language governing permissions and
100262 * limitations under the License.
100263 */
100264
100265 namespace JMS\Serializer\Annotation;
100266
100267 /**
100268 * @Annotation
100269 * @Target("METHOD")
100270 *
100271 * @author Johannes M. Schmitt <schmittjoh@gmail.com>
100272 */
100273 final class HandlerCallback
100274 {
100275 /**
100276 * @Required
100277 * @var string
100278 */
100279 public $format;
100280
100281 /**
100282 * @Required
100283 * @var string
100284 */
100285 public $direction;
100286 }
100287 <?php
100288
100289 /*
100290 * Copyright 2013 Johannes M. Schmitt <schmittjoh@gmail.com>
100291 *
100292 * Licensed under the Apache License, Version 2.0 (the "License");
100293 * you may not use this file except in compliance with the License.
100294 * You may obtain a copy of the License at
100295 *
100296 * http://www.apache.org/licenses/LICENSE-2.0
100297 *
100298 * Unless required by applicable law or agreed to in writing, software
100299 * distributed under the License is distributed on an "AS IS" BASIS,
100300 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
100301 * See the License for the specific language governing permissions and
100302 * limitations under the License.
100303 */
100304
100305 namespace JMS\Serializer\Annotation;
100306
100307 /**
100308 * @Annotation
100309 * @Target("METHOD")
100310 */
100311 final class PostSerialize
100312 {
100313 }
100314 <?php
100315
100316 /*
100317 * Copyright 2013 Johannes M. Schmitt <schmittjoh@gmail.com>
100318 *
100319 * Licensed under the Apache License, Version 2.0 (the "License");
100320 * you may not use this file except in compliance with the License.
100321 * You may obtain a copy of the License at
100322 *
100323 * http://www.apache.org/licenses/LICENSE-2.0
100324 *
100325 * Unless required by applicable law or agreed to in writing, software
100326 * distributed under the License is distributed on an "AS IS" BASIS,
100327 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
100328 * See the License for the specific language governing permissions and
100329 * limitations under the License.
100330 */
100331
100332 namespace JMS\Serializer\Annotation;
100333
100334 /**
100335 * @Annotation
100336 * @Target({"PROPERTY", "CLASS"})
100337 */
100338 final class Exclude
100339 {
100340 }
100341 <?php
100342
100343 /*
100344 * Copyright 2013 Johannes M. Schmitt <schmittjoh@gmail.com>
100345 *
100346 * Licensed under the Apache License, Version 2.0 (the "License");
100347 * you may not use this file except in compliance with the License.
100348 * You may obtain a copy of the License at
100349 *
100350 * http://www.apache.org/licenses/LICENSE-2.0
100351 *
100352 * Unless required by applicable law or agreed to in writing, software
100353 * distributed under the License is distributed on an "AS IS" BASIS,
100354 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
100355 * See the License for the specific language governing permissions and
100356 * limitations under the License.
100357 */
100358
100359 namespace JMS\Serializer\Annotation;
100360
100361 /**
100362 * This annotation can be defined on methods which are called after the
100363 * deserialization of the object is complete.
100364 *
100365 * These methods do not necessarily have to be public.
100366 *
100367 * @Annotation
100368 * @Target("METHOD")
100369 * @author Johannes M. Schmitt <schmittjoh@gmail.com>
100370 */
100371 final class PostDeserialize
100372 {
100373 }
100374 <?php
100375
100376 /*
100377 * Copyright 2013 Johannes M. Schmitt <schmittjoh@gmail.com>
100378 *
100379 * Licensed under the Apache License, Version 2.0 (the "License");
100380 * you may not use this file except in compliance with the License.
100381 * You may obtain a copy of the License at
100382 *
100383 * http://www.apache.org/licenses/LICENSE-2.0
100384 *
100385 * Unless required by applicable law or agreed to in writing, software
100386 * distributed under the License is distributed on an "AS IS" BASIS,
100387 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
100388 * See the License for the specific language governing permissions and
100389 * limitations under the License.
100390 */
100391
100392 namespace JMS\Serializer\Annotation;
100393
100394 /**
100395 * @Annotation
100396 * @Target("CLASS")
100397 */
100398 final class XmlRoot
100399 {
100400 /**
100401 * @Required
100402 * @var string
100403 */
100404 public $name;
100405 }
100406 <?php
100407
100408 /*
100409 * Copyright 2013 Johannes M. Schmitt <schmittjoh@gmail.com>
100410 *
100411 * Licensed under the Apache License, Version 2.0 (the "License");
100412 * you may not use this file except in compliance with the License.
100413 * You may obtain a copy of the License at
100414 *
100415 * http://www.apache.org/licenses/LICENSE-2.0
100416 *
100417 * Unless required by applicable law or agreed to in writing, software
100418 * distributed under the License is distributed on an "AS IS" BASIS,
100419 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
100420 * See the License for the specific language governing permissions and
100421 * limitations under the License.
100422 */
100423
100424 namespace JMS\Serializer\Annotation;
100425
100426 /**
100427 * @Annotation
100428 * @Target({"PROPERTY","METHOD"})
100429 */
100430 final class XmlValue
100431 {
100432 }
100433 <?php
100434
100435 /*
100436 * Copyright 2013 Johannes M. Schmitt <schmittjoh@gmail.com>
100437 *
100438 * Licensed under the Apache License, Version 2.0 (the "License");
100439 * you may not use this file except in compliance with the License.
100440 * You may obtain a copy of the License at
100441 *
100442 * http://www.apache.org/licenses/LICENSE-2.0
100443 *
100444 * Unless required by applicable law or agreed to in writing, software
100445 * distributed under the License is distributed on an "AS IS" BASIS,
100446 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
100447 * See the License for the specific language governing permissions and
100448 * limitations under the License.
100449 */
100450
100451 namespace JMS\Serializer\Annotation;
100452
100453 /**
100454 * @Annotation
100455 * @Target({"PROPERTY","METHOD"})
100456 */
100457 final class XmlList extends XmlCollection
100458 {
100459 }
100460 <?php
100461
100462 /*
100463 * Copyright 2013 Johannes M. Schmitt <schmittjoh@gmail.com>
100464 *
100465 * Licensed under the Apache License, Version 2.0 (the "License");
100466 * you may not use this file except in compliance with the License.
100467 * You may obtain a copy of the License at
100468 *
100469 * http://www.apache.org/licenses/LICENSE-2.0
100470 *
100471 * Unless required by applicable law or agreed to in writing, software
100472 * distributed under the License is distributed on an "AS IS" BASIS,
100473 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
100474 * See the License for the specific language governing permissions and
100475 * limitations under the License.
100476 */
100477
100478 namespace JMS\Serializer\Annotation;
100479
100480 /**
100481 * @Annotation
100482 * @Target({"PROPERTY", "METHOD"})
100483 */
100484 final class Until extends Version
100485 {
100486 }
100487 <?php
100488
100489 /*
100490 * Copyright 2013 Johannes M. Schmitt <schmittjoh@gmail.com>
100491 *
100492 * Licensed under the Apache License, Version 2.0 (the "License");
100493 * you may not use this file except in compliance with the License.
100494 * You may obtain a copy of the License at
100495 *
100496 * http://www.apache.org/licenses/LICENSE-2.0
100497 *
100498 * Unless required by applicable law or agreed to in writing, software
100499 * distributed under the License is distributed on an "AS IS" BASIS,
100500 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
100501 * See the License for the specific language governing permissions and
100502 * limitations under the License.
100503 */
100504
100505 namespace JMS\Serializer\Annotation;
100506
100507 /**
100508 * @Annotation
100509 * @Target({"PROPERTY","METHOD"})
100510 */
100511 final class Groups
100512 {
100513 /** @var array<string> @Required */
100514 public $groups;
100515 }
100516 <?php
100517
100518 /*
100519 * Copyright 2013 Johannes M. Schmitt <schmittjoh@gmail.com>
100520 *
100521 * Licensed under the Apache License, Version 2.0 (the "License");
100522 * you may not use this file except in compliance with the License.
100523 * You may obtain a copy of the License at
100524 *
100525 * http://www.apache.org/licenses/LICENSE-2.0
100526 *
100527 * Unless required by applicable law or agreed to in writing, software
100528 * distributed under the License is distributed on an "AS IS" BASIS,
100529 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
100530 * See the License for the specific language governing permissions and
100531 * limitations under the License.
100532 */
100533
100534 namespace JMS\Serializer\Annotation;
100535
100536 /**
100537 * Controls the order of properties in a class.
100538 *
100539 * @Annotation
100540 * @Target("CLASS")
100541 * @author Johannes M. Schmitt <schmittjoh@gmail.com>
100542 */
100543 final class AccessorOrder
100544 {
100545 /**
100546 * @Required
100547 * @var string
100548 */
100549 public $order;
100550
100551 /**
100552 * @var array<string>
100553 */
100554 public $custom = array();
100555 }
100556 <?php
100557
100558 /*
100559 * Copyright 2013 Johannes M. Schmitt <schmittjoh@gmail.com>
100560 *
100561 * Licensed under the Apache License, Version 2.0 (the "License");
100562 * you may not use this file except in compliance with the License.
100563 * You may obtain a copy of the License at
100564 *
100565 * http://www.apache.org/licenses/LICENSE-2.0
100566 *
100567 * Unless required by applicable law or agreed to in writing, software
100568 * distributed under the License is distributed on an "AS IS" BASIS,
100569 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
100570 * See the License for the specific language governing permissions and
100571 * limitations under the License.
100572 */
100573
100574 namespace JMS\Serializer\Annotation;
100575
100576 /**
100577 * @Annotation
100578 * @Target("METHOD")
100579 *
100580 * @author Alexander Klimenkov <alx.devel@gmail.com>
100581 */
100582 final class VirtualProperty
100583 {
100584 }
100585 <?php
100586
100587 /*
100588 * Copyright 2013 Johannes M. Schmitt <schmittjoh@gmail.com>
100589 *
100590 * Licensed under the Apache License, Version 2.0 (the "License");
100591 * you may not use this file except in compliance with the License.
100592 * You may obtain a copy of the License at
100593 *
100594 * http://www.apache.org/licenses/LICENSE-2.0
100595 *
100596 * Unless required by applicable law or agreed to in writing, software
100597 * distributed under the License is distributed on an "AS IS" BASIS,
100598 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
100599 * See the License for the specific language governing permissions and
100600 * limitations under the License.
100601 */
100602
100603 namespace JMS\Serializer\Annotation;
100604
100605 /**
100606 * @Annotation
100607 * @Target({"PROPERTY", "METHOD"})
100608 */
100609 final class Since extends Version
100610 {
100611 }
100612 <?php
100613
100614 /*
100615 * Copyright 2011 Johannes M. Schmitt <schmittjoh@gmail.com>
100616 *
100617 * Licensed under the Apache License, Version 2.0 (the "License");
100618 * you may not use this file except in compliance with the License.
100619 * You may obtain a copy of the License at
100620 *
100621 * http://www.apache.org/licenses/LICENSE-2.0
100622 *
100623 * Unless required by applicable law or agreed to in writing, software
100624 * distributed under the License is distributed on an "AS IS" BASIS,
100625 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
100626 * See the License for the specific language governing permissions and
100627 * limitations under the License.
100628 */
100629
100630 namespace JMS\Serializer\Annotation;
100631
100632 /**
100633 * @Annotation
100634 * @Target({"PROPERTY","METHOD"})
100635 */
100636 final class MaxDepth
100637 {
100638 /**
100639 * @Required
100640 * @var integer
100641 */
100642 public $depth;
100643 }
100644 <?php
100645
100646 /*
100647 * Copyright 2013 Johannes M. Schmitt <schmittjoh@gmail.com>
100648 *
100649 * Licensed under the Apache License, Version 2.0 (the "License");
100650 * you may not use this file except in compliance with the License.
100651 * You may obtain a copy of the License at
100652 *
100653 * http://www.apache.org/licenses/LICENSE-2.0
100654 *
100655 * Unless required by applicable law or agreed to in writing, software
100656 * distributed under the License is distributed on an "AS IS" BASIS,
100657 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
100658 * See the License for the specific language governing permissions and
100659 * limitations under the License.
100660 */
100661
100662 namespace JMS\Serializer\Annotation;
100663
100664 /**
100665 * @Annotation
100666 * @Target("CLASS")
100667 */
100668 class Discriminator
100669 {
100670 /** @var array<string> */
100671 public $map;
100672
100673 /** @var string */
100674 public $field = 'type';
100675
100676 /** @var boolean */
100677 public $disabled = false;
100678 }<?php
100679
100680 /*
100681 * Copyright 2013 Johannes M. Schmitt <schmittjoh@gmail.com>
100682 *
100683 * Licensed under the Apache License, Version 2.0 (the "License");
100684 * you may not use this file except in compliance with the License.
100685 * You may obtain a copy of the License at
100686 *
100687 * http://www.apache.org/licenses/LICENSE-2.0
100688 *
100689 * Unless required by applicable law or agreed to in writing, software
100690 * distributed under the License is distributed on an "AS IS" BASIS,
100691 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
100692 * See the License for the specific language governing permissions and
100693 * limitations under the License.
100694 */
100695
100696 namespace JMS\Serializer\Annotation;
100697
100698 /**
100699 * @Annotation
100700 * @Target({"PROPERTY", "METHOD"})
100701 */
100702 final class Type
100703 {
100704 /**
100705 * @Required
100706 * @var string
100707 */
100708 public $name;
100709 }
100710 <?php
100711
100712 /*
100713 * Copyright 2013 Johannes M. Schmitt <schmittjoh@gmail.com>
100714 *
100715 * Licensed under the Apache License, Version 2.0 (the "License");
100716 * you may not use this file except in compliance with the License.
100717 * You may obtain a copy of the License at
100718 *
100719 * http://www.apache.org/licenses/LICENSE-2.0
100720 *
100721 * Unless required by applicable law or agreed to in writing, software
100722 * distributed under the License is distributed on an "AS IS" BASIS,
100723 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
100724 * See the License for the specific language governing permissions and
100725 * limitations under the License.
100726 */
100727
100728 namespace JMS\Serializer\Annotation;
100729
100730 /**
100731 * @Annotation
100732 * @Target("PROPERTY")
100733 *
100734 * @author Johannes M. Schmitt <schmittjoh@gmail.com>
100735 */
100736 final class Accessor
100737 {
100738 /**
100739 * @var string
100740 */
100741 public $getter;
100742
100743 /**
100744 * @var string
100745 */
100746 public $setter;
100747 }
100748 <?php
100749
100750 /*
100751 * Copyright 2013 Johannes M. Schmitt <schmittjoh@gmail.com>
100752 *
100753 * Licensed under the Apache License, Version 2.0 (the "License");
100754 * you may not use this file except in compliance with the License.
100755 * You may obtain a copy of the License at
100756 *
100757 * http://www.apache.org/licenses/LICENSE-2.0
100758 *
100759 * Unless required by applicable law or agreed to in writing, software
100760 * distributed under the License is distributed on an "AS IS" BASIS,
100761 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
100762 * See the License for the specific language governing permissions and
100763 * limitations under the License.
100764 */
100765
100766 namespace JMS\Serializer\Annotation;
100767
100768 use JMS\Serializer\Exception\RuntimeException;
100769
100770 /**
100771 * @Annotation
100772 * @Target("CLASS")
100773 */
100774 final class ExclusionPolicy
100775 {
100776 const NONE = 'NONE';
100777 const ALL = 'ALL';
100778
100779 public $policy;
100780
100781 public function __construct(array $values)
100782 {
100783 if (!is_string($values['value'])) {
100784 throw new RuntimeException('"value" must be a string.');
100785 }
100786
100787 $this->policy = strtoupper($values['value']);
100788
100789 if (self::NONE !== $this->policy && self::ALL !== $this->policy) {
100790 throw new RuntimeException('Exclusion policy must either be "ALL", or "NONE".');
100791 }
100792 }
100793 }
100794 <?php
100795
100796 /*
100797 * Copyright 2013 Johannes M. Schmitt <schmittjoh@gmail.com>
100798 *
100799 * Licensed under the Apache License, Version 2.0 (the "License");
100800 * you may not use this file except in compliance with the License.
100801 * You may obtain a copy of the License at
100802 *
100803 * http://www.apache.org/licenses/LICENSE-2.0
100804 *
100805 * Unless required by applicable law or agreed to in writing, software
100806 * distributed under the License is distributed on an "AS IS" BASIS,
100807 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
100808 * See the License for the specific language governing permissions and
100809 * limitations under the License.
100810 */
100811
100812 namespace JMS\Serializer\Annotation;
100813
100814 /**
100815 * @Annotation
100816 * @Target({"PROPERTY","METHOD"})
100817 */
100818 final class XmlKeyValuePairs
100819 {
100820 }
100821 <?php
100822
100823 /*
100824 * Copyright 2013 Johannes M. Schmitt <schmittjoh@gmail.com>
100825 *
100826 * Licensed under the Apache License, Version 2.0 (the "License");
100827 * you may not use this file except in compliance with the License.
100828 * You may obtain a copy of the License at
100829 *
100830 * http://www.apache.org/licenses/LICENSE-2.0
100831 *
100832 * Unless required by applicable law or agreed to in writing, software
100833 * distributed under the License is distributed on an "AS IS" BASIS,
100834 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
100835 * See the License for the specific language governing permissions and
100836 * limitations under the License.
100837 */
100838
100839 namespace JMS\Serializer\Annotation;
100840
100841 /**
100842 * @Annotation
100843 * @Target({"PROPERTY","METHOD"})
100844 */
100845 final class Inline
100846 {
100847 }
100848 <?php
100849
100850 /*
100851 * Copyright 2013 Johannes M. Schmitt <schmittjoh@gmail.com>
100852 *
100853 * Licensed under the Apache License, Version 2.0 (the "License");
100854 * you may not use this file except in compliance with the License.
100855 * You may obtain a copy of the License at
100856 *
100857 * http://www.apache.org/licenses/LICENSE-2.0
100858 *
100859 * Unless required by applicable law or agreed to in writing, software
100860 * distributed under the License is distributed on an "AS IS" BASIS,
100861 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
100862 * See the License for the specific language governing permissions and
100863 * limitations under the License.
100864 */
100865
100866 namespace JMS\Serializer\Annotation;
100867
100868 /**
100869 * @Annotation
100870 * @Target("PROPERTY")
100871 */
100872 final class ReadOnly
100873 {
100874 }
100875 <?php
100876
100877 /*
100878 * Copyright 2013 Johannes M. Schmitt <schmittjoh@gmail.com>
100879 *
100880 * Licensed under the Apache License, Version 2.0 (the "License");
100881 * you may not use this file except in compliance with the License.
100882 * You may obtain a copy of the License at
100883 *
100884 * http://www.apache.org/licenses/LICENSE-2.0
100885 *
100886 * Unless required by applicable law or agreed to in writing, software
100887 * distributed under the License is distributed on an "AS IS" BASIS,
100888 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
100889 * See the License for the specific language governing permissions and
100890 * limitations under the License.
100891 */
100892
100893 namespace JMS\Serializer\Annotation;
100894
100895 /**
100896 * @Annotation
100897 * @Target("PROPERTY")
100898 */
100899 final class Expose
100900 {
100901 }
100902 <?php
100903
100904 /*
100905 * Copyright 2013 Johannes M. Schmitt <schmittjoh@gmail.com>
100906 *
100907 * Licensed under the Apache License, Version 2.0 (the "License");
100908 * you may not use this file except in compliance with the License.
100909 * You may obtain a copy of the License at
100910 *
100911 * http://www.apache.org/licenses/LICENSE-2.0
100912 *
100913 * Unless required by applicable law or agreed to in writing, software
100914 * distributed under the License is distributed on an "AS IS" BASIS,
100915 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
100916 * See the License for the specific language governing permissions and
100917 * limitations under the License.
100918 */
100919
100920 namespace JMS\Serializer\Metadata;
100921
100922
100923 class VirtualPropertyMetadata extends PropertyMetadata
100924 {
100925 public function __construct($class, $methodName)
100926 {
100927 if (0 === strpos($methodName, 'get')) {
100928 $fieldName = lcfirst(substr($methodName, 3));
100929 } else {
100930 $fieldName = $methodName;
100931 }
100932
100933 $this->class = $class;
100934 $this->name = $fieldName;
100935 $this->getter = $methodName;
100936 $this->readOnly = true;
100937 }
100938
100939 public function setValue($obj, $value)
100940 {
100941 throw new \LogicException('VirtualPropertyMetadata is immutable.');
100942 }
100943
100944 public function setAccessor($type, $getter = null, $setter = null)
100945 {
100946 }
100947
100948 public function serialize()
100949 {
100950 return serialize(array(
100951 $this->sinceVersion,
100952 $this->untilVersion,
100953 $this->groups,
100954 $this->serializedName,
100955 $this->type,
100956 $this->xmlCollection,
100957 $this->xmlCollectionInline,
100958 $this->xmlEntryName,
100959 $this->xmlKeyAttribute,
100960 $this->xmlAttribute,
100961 $this->xmlValue,
100962 $this->xmlKeyValuePairs,
100963 $this->getter,
100964 $this->setter,
100965 $this->inline,
100966 $this->readOnly,
100967 $this->class,
100968 $this->name
100969 ));
100970 }
100971
100972 public function unserialize($str)
100973 {
100974 list(
100975 $this->sinceVersion,
100976 $this->untilVersion,
100977 $this->groups,
100978 $this->serializedName,
100979 $this->type,
100980 $this->xmlCollection,
100981 $this->xmlCollectionInline,
100982 $this->xmlEntryName,
100983 $this->xmlKeyAttribute,
100984 $this->xmlAttribute,
100985 $this->xmlValue,
100986 $this->xmlKeyValuePairs,
100987 $this->getter,
100988 $this->setter,
100989 $this->inline,
100990 $this->readOnly,
100991 $this->class,
100992 $this->name
100993 ) = unserialize($str);
100994 }
100995 }
100996 <?php
100997
100998 /*
100999 * Copyright 2013 Johannes M. Schmitt <schmittjoh@gmail.com>
101000 *
101001 * Licensed under the Apache License, Version 2.0 (the "License");
101002 * you may not use this file except in compliance with the License.
101003 * You may obtain a copy of the License at
101004 *
101005 * http://www.apache.org/licenses/LICENSE-2.0
101006 *
101007 * Unless required by applicable law or agreed to in writing, software
101008 * distributed under the License is distributed on an "AS IS" BASIS,
101009 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
101010 * See the License for the specific language governing permissions and
101011 * limitations under the License.
101012 */
101013
101014 namespace JMS\Serializer\Metadata\Driver;
101015
101016 use JMS\Serializer\Annotation\Discriminator;
101017 use JMS\Serializer\GraphNavigator;
101018 use JMS\Serializer\Annotation\HandlerCallback;
101019 use JMS\Serializer\Annotation\AccessorOrder;
101020 use JMS\Serializer\Annotation\Accessor;
101021 use JMS\Serializer\Annotation\AccessType;
101022 use JMS\Serializer\Annotation\XmlMap;
101023 use JMS\Serializer\Annotation\XmlRoot;
101024 use JMS\Serializer\Annotation\XmlAttribute;
101025 use JMS\Serializer\Annotation\XmlList;
101026 use JMS\Serializer\Annotation\XmlValue;
101027 use JMS\Serializer\Annotation\XmlKeyValuePairs;
101028 use JMS\Serializer\Annotation\PostSerialize;
101029 use JMS\Serializer\Annotation\PostDeserialize;
101030 use JMS\Serializer\Annotation\PreSerialize;
101031 use JMS\Serializer\Annotation\VirtualProperty;
101032 use Metadata\MethodMetadata;
101033 use Doctrine\Common\Annotations\Reader;
101034 use JMS\Serializer\Annotation\Type;
101035 use JMS\Serializer\Annotation\Exclude;
101036 use JMS\Serializer\Annotation\Groups;
101037 use JMS\Serializer\Annotation\Expose;
101038 use JMS\Serializer\Annotation\SerializedName;
101039 use JMS\Serializer\Annotation\Until;
101040 use JMS\Serializer\Annotation\Since;
101041 use JMS\Serializer\Annotation\ExclusionPolicy;
101042 use JMS\Serializer\Annotation\Inline;
101043 use JMS\Serializer\Annotation\ReadOnly;
101044 use JMS\Serializer\Metadata\ClassMetadata;
101045 use JMS\Serializer\Metadata\PropertyMetadata;
101046 use JMS\Serializer\Metadata\VirtualPropertyMetadata;
101047 use JMS\Serializer\Exception\InvalidArgumentException;
101048 use JMS\Serializer\Annotation\XmlAttributeMap;
101049 use Metadata\Driver\DriverInterface;
101050 use JMS\Serializer\Annotation\MaxDepth;
101051
101052 class AnnotationDriver implements DriverInterface
101053 {
101054 private $reader;
101055
101056 public function __construct(Reader $reader)
101057 {
101058 $this->reader = $reader;
101059 }
101060
101061 public function loadMetadataForClass(\ReflectionClass $class)
101062 {
101063 $classMetadata = new ClassMetadata($name = $class->name);
101064 $classMetadata->fileResources[] = $class->getFilename();
101065
101066 $propertiesMetadata = array();
101067 $propertiesAnnotations = array();
101068
101069 $exclusionPolicy = 'NONE';
101070 $excludeAll = false;
101071 $classAccessType = PropertyMetadata::ACCESS_TYPE_PROPERTY;
101072 foreach ($this->reader->getClassAnnotations($class) as $annot) {
101073 if ($annot instanceof ExclusionPolicy) {
101074 $exclusionPolicy = $annot->policy;
101075 } elseif ($annot instanceof XmlRoot) {
101076 $classMetadata->xmlRootName = $annot->name;
101077 } elseif ($annot instanceof Exclude) {
101078 $excludeAll = true;
101079 } elseif ($annot instanceof AccessType) {
101080 $classAccessType = $annot->type;
101081 } elseif ($annot instanceof AccessorOrder) {
101082 $classMetadata->setAccessorOrder($annot->order, $annot->custom);
101083 } elseif ($annot instanceof Discriminator) {
101084 if ($annot->disabled) {
101085 $classMetadata->discriminatorDisabled = true;
101086 } else {
101087 $classMetadata->setDiscriminator($annot->field, $annot->map);
101088 }
101089 }
101090 }
101091
101092 foreach ($class->getMethods() as $method) {
101093 if ($method->class !== $name) {
101094 continue;
101095 }
101096
101097 $methodAnnotations = $this->reader->getMethodAnnotations($method);
101098
101099 foreach ($methodAnnotations as $annot) {
101100 if ($annot instanceof PreSerialize) {
101101 $classMetadata->addPreSerializeMethod(new MethodMetadata($name, $method->name));
101102 continue 2;
101103 } elseif ($annot instanceof PostDeserialize) {
101104 $classMetadata->addPostDeserializeMethod(new MethodMetadata($name, $method->name));
101105 continue 2;
101106 } elseif ($annot instanceof PostSerialize) {
101107 $classMetadata->addPostSerializeMethod(new MethodMetadata($name, $method->name));
101108 continue 2;
101109 } elseif ($annot instanceof VirtualProperty) {
101110 $virtualPropertyMetadata = new VirtualPropertyMetadata($name, $method->name);
101111 $propertiesMetadata[] = $virtualPropertyMetadata;
101112 $propertiesAnnotations[] = $methodAnnotations;
101113 continue 2;
101114 } elseif ($annot instanceof HandlerCallback) {
101115 $classMetadata->addHandlerCallback(GraphNavigator::parseDirection($annot->direction), $annot->format, $method->name);
101116 continue 2;
101117 }
101118 }
101119 }
101120
101121 if (!$excludeAll) {
101122 foreach ($class->getProperties() as $property) {
101123 if ($property->class !== $name) {
101124 continue;
101125 }
101126 $propertiesMetadata[] = new PropertyMetadata($name, $property->getName());
101127 $propertiesAnnotations[] = $this->reader->getPropertyAnnotations($property);
101128 }
101129
101130 foreach ($propertiesMetadata as $propertyKey => $propertyMetadata) {
101131
101132 $isExclude = false;
101133 $isExpose = $propertyMetadata instanceof VirtualPropertyMetadata;
101134 $accessType = $classAccessType;
101135 $accessor = array(null, null);
101136
101137 $propertyAnnotations = $propertiesAnnotations[$propertyKey];
101138
101139 foreach ($propertyAnnotations as $annot) {
101140 if ($annot instanceof Since) {
101141 $propertyMetadata->sinceVersion = $annot->version;
101142 } elseif ($annot instanceof Until) {
101143 $propertyMetadata->untilVersion = $annot->version;
101144 } elseif ($annot instanceof SerializedName) {
101145 $propertyMetadata->serializedName = $annot->name;
101146 } elseif ($annot instanceof Expose) {
101147 $isExpose = true;
101148 } elseif ($annot instanceof Exclude) {
101149 $isExclude = true;
101150 } elseif ($annot instanceof Type) {
101151 $propertyMetadata->setType($annot->name);
101152 } elseif ($annot instanceof XmlList) {
101153 $propertyMetadata->xmlCollection = true;
101154 $propertyMetadata->xmlCollectionInline = $annot->inline;
101155 $propertyMetadata->xmlEntryName = $annot->entry;
101156 } elseif ($annot instanceof XmlMap) {
101157 $propertyMetadata->xmlCollection = true;
101158 $propertyMetadata->xmlCollectionInline = $annot->inline;
101159 $propertyMetadata->xmlEntryName = $annot->entry;
101160 $propertyMetadata->xmlKeyAttribute = $annot->keyAttribute;
101161 } elseif ($annot instanceof XmlKeyValuePairs) {
101162 $propertyMetadata->xmlKeyValuePairs = true;
101163 } elseif ($annot instanceof XmlAttribute) {
101164 $propertyMetadata->xmlAttribute = true;
101165 } elseif ($annot instanceof XmlValue) {
101166 $propertyMetadata->xmlValue = true;
101167 } elseif ($annot instanceof AccessType) {
101168 $accessType = $annot->type;
101169 } elseif ($annot instanceof ReadOnly) {
101170 $propertyMetadata->readOnly = true;
101171 } elseif ($annot instanceof Accessor) {
101172 $accessor = array($annot->getter, $annot->setter);
101173 } elseif ($annot instanceof Groups) {
101174 $propertyMetadata->groups = $annot->groups;
101175 foreach ((array) $propertyMetadata->groups as $groupName) {
101176 if (false !== strpos($groupName, ',')) {
101177 throw new InvalidArgumentException(sprintf(
101178 'Invalid group name "%s" on "%s", did you mean to create multiple groups?',
101179 implode(', ', $propertyMetadata->groups),
101180 $propertyMetadata->class.'->'.$propertyMetadata->name
101181 ));
101182 }
101183 }
101184 } elseif ($annot instanceof Inline) {
101185 $propertyMetadata->inline = true;
101186 } elseif ($annot instanceof XmlAttributeMap) {
101187 $propertyMetadata->xmlAttributeMap = true;
101188 } elseif ($annot instanceof MaxDepth) {
101189 $propertyMetadata->maxDepth = $annot->depth;
101190 }
101191 }
101192
101193 $propertyMetadata->setAccessor($accessType, $accessor[0], $accessor[1]);
101194
101195 if ((ExclusionPolicy::NONE === $exclusionPolicy && !$isExclude)
101196 || (ExclusionPolicy::ALL === $exclusionPolicy && $isExpose)) {
101197 $classMetadata->addPropertyMetadata($propertyMetadata);
101198 }
101199 }
101200 }
101201
101202 return $classMetadata;
101203 }
101204 }
101205 <?php
101206
101207 /*
101208 * Copyright 2013 Johannes M. Schmitt <schmittjoh@gmail.com>
101209 *
101210 * Licensed under the Apache License, Version 2.0 (the "License");
101211 * you may not use this file except in compliance with the License.
101212 * You may obtain a copy of the License at
101213 *
101214 * http://www.apache.org/licenses/LICENSE-2.0
101215 *
101216 * Unless required by applicable law or agreed to in writing, software
101217 * distributed under the License is distributed on an "AS IS" BASIS,
101218 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
101219 * See the License for the specific language governing permissions and
101220 * limitations under the License.
101221 */
101222
101223 namespace JMS\Serializer\Metadata\Driver;
101224
101225 use JMS\Serializer\GraphNavigator;
101226 use JMS\Serializer\Exception\RuntimeException;
101227 use JMS\Serializer\Exception\XmlErrorException;
101228 use JMS\Serializer\Annotation\ExclusionPolicy;
101229 use JMS\Serializer\Metadata\PropertyMetadata;
101230 use JMS\Serializer\Metadata\VirtualPropertyMetadata;
101231 use Metadata\MethodMetadata;
101232 use JMS\Serializer\Metadata\ClassMetadata;
101233 use Metadata\Driver\AbstractFileDriver;
101234
101235 class XmlDriver extends AbstractFileDriver
101236 {
101237 protected function loadMetadataFromFile(\ReflectionClass $class, $path)
101238 {
101239 $previous = libxml_use_internal_errors(true);
101240 $elem = simplexml_load_file($path);
101241 libxml_use_internal_errors($previous);
101242
101243 if (false === $elem) {
101244 throw new XmlErrorException(libxml_get_last_error());
101245 }
101246
101247 $metadata = new ClassMetadata($name = $class->name);
101248 if (!$elems = $elem->xpath("./class[@name = '".$name."']")) {
101249 throw new RuntimeException(sprintf('Could not find class %s inside XML element.', $name));
101250 }
101251 $elem = reset($elems);
101252
101253 $metadata->fileResources[] = $path;
101254 $metadata->fileResources[] = $class->getFileName();
101255 $exclusionPolicy = strtoupper($elem->attributes()->{'exclusion-policy'}) ?: 'NONE';
101256 $excludeAll = null !== ($exclude = $elem->attributes()->exclude) ? 'true' === strtolower($exclude) : false;
101257 $classAccessType = (string) ($elem->attributes()->{'access-type'} ?: PropertyMetadata::ACCESS_TYPE_PROPERTY);
101258
101259 $propertiesMetadata = array();
101260 $propertiesNodes = array();
101261
101262 if (null !== $accessorOrder = $elem->attributes()->{'accessor-order'}) {
101263 $metadata->setAccessorOrder((string) $accessorOrder, preg_split('/\s*,\s*/', (string) $elem->attributes()->{'custom-accessor-order'}));
101264 }
101265
101266 if (null !== $xmlRootName = $elem->attributes()->{'xml-root-name'}) {
101267 $metadata->xmlRootName = (string) $xmlRootName;
101268 }
101269
101270 $discriminatorFieldName = (string) $elem->attributes()->{'discriminator-field-name'};
101271 $discriminatorMap = array();
101272 foreach ($elem->xpath('./discriminator-class') as $entry) {
101273 if ( ! isset($entry->attributes()->value)) {
101274 throw new RuntimeException('Each discriminator-class element must have a "value" attribute.');
101275 }
101276
101277 $discriminatorMap[(string) $entry->attributes()->value] = (string) $entry;
101278 }
101279
101280 if ('true' === (string) $elem->attributes()->{'discriminator-disabled'}) {
101281 $metadata->discriminatorDisabled = true;
101282 } elseif ( ! empty($discriminatorFieldName) || ! empty($discriminatorMap)) {
101283 $metadata->setDiscriminator($discriminatorFieldName, $discriminatorMap);
101284 }
101285
101286 foreach ($elem->xpath('./virtual-property') as $method) {
101287 if (!isset($method->attributes()->method)) {
101288 throw new RuntimeException('The method attribute must be set for all virtual-property elements.');
101289 }
101290
101291 $virtualPropertyMetadata = new VirtualPropertyMetadata( $name, (string) $method->attributes()->method );
101292
101293 $propertiesMetadata[] = $virtualPropertyMetadata;
101294 $propertiesNodes[] = $method;
101295 }
101296
101297 if (!$excludeAll) {
101298
101299 foreach ($class->getProperties() as $property) {
101300 if ($name !== $property->class) {
101301 continue;
101302 }
101303
101304 $propertiesMetadata[] = new PropertyMetadata($name, $pName = $property->getName());
101305 $pElems = $elem->xpath("./property[@name = '".$pName."']");
101306
101307 $propertiesNodes[] = $pElems ? reset( $pElems ) : null;
101308 }
101309
101310 foreach ($propertiesMetadata as $propertyKey => $pMetadata) {
101311
101312 $isExclude = false;
101313 $isExpose = $pMetadata instanceof VirtualPropertyMetadata;
101314
101315 $pElem = $propertiesNodes[$propertyKey];
101316 if (!empty( $pElem )) {
101317
101318 if (null !== $exclude = $pElem->attributes()->exclude) {
101319 $isExclude = 'true' === strtolower($exclude);
101320 }
101321
101322 if (null !== $expose = $pElem->attributes()->expose) {
101323 $isExpose = 'true' === strtolower($expose);
101324 }
101325
101326 if (null !== $version = $pElem->attributes()->{'since-version'}) {
101327 $pMetadata->sinceVersion = (string) $version;
101328 }
101329
101330 if (null !== $version = $pElem->attributes()->{'until-version'}) {
101331 $pMetadata->untilVersion = (string) $version;
101332 }
101333
101334 if (null !== $serializedName = $pElem->attributes()->{'serialized-name'}) {
101335 $pMetadata->serializedName = (string) $serializedName;
101336 }
101337
101338 if (null !== $type = $pElem->attributes()->type) {
101339 $pMetadata->setType((string) $type);
101340 } elseif (isset($pElem->type)) {
101341 $pMetadata->setType((string) $pElem->type);
101342 }
101343
101344 if (null !== $groups = $pElem->attributes()->groups) {
101345 $pMetadata->groups = preg_split('/\s*,\s*/', (string) $groups);
101346 }
101347
101348 if (isset($pElem->{'xml-list'})) {
101349 $pMetadata->xmlCollection = true;
101350
101351 $colConfig = $pElem->{'xml-list'};
101352 if (isset($colConfig->attributes()->inline)) {
101353 $pMetadata->xmlCollectionInline = 'true' === (string) $colConfig->attributes()->inline;
101354 }
101355
101356 if (isset($colConfig->attributes()->{'entry-name'})) {
101357 $pMetadata->xmlEntryName = (string) $colConfig->attributes()->{'entry-name'};
101358 }
101359 }
101360
101361 if (isset($pElem->{'xml-map'})) {
101362 $pMetadata->xmlCollection = true;
101363
101364 $colConfig = $pElem->{'xml-map'};
101365 if (isset($colConfig->attributes()->inline)) {
101366 $pMetadata->xmlCollectionInline = 'true' === (string) $colConfig->attributes()->inline;
101367 }
101368
101369 if (isset($colConfig->attributes()->{'entry-name'})) {
101370 $pMetadata->xmlEntryName = (string) $colConfig->attributes()->{'entry-name'};
101371 }
101372
101373 if (isset($colConfig->attributes()->{'key-attribute-name'})) {
101374 $pMetadata->xmlKeyAttribute = (string) $colConfig->attributes()->{'key-attribute-name'};
101375 }
101376 }
101377
101378 if (isset($pElem->attributes()->{'xml-attribute'})) {
101379 $pMetadata->xmlAttribute = 'true' === (string) $pElem->attributes()->{'xml-attribute'};
101380 }
101381
101382 if (isset($pElem->attributes()->{'xml-attribute-map'})) {
101383 $pMetadata->xmlAttribute = 'true' === (string) $pElem->attributes()->{'xml-attribute-map'};
101384 }
101385
101386 if (isset($pElem->attributes()->{'xml-value'})) {
101387 $pMetadata->xmlValue = 'true' === (string) $pElem->attributes()->{'xml-value'};
101388 }
101389
101390 if (isset($pElem->attributes()->{'xml-key-value-pairs'})) {
101391 $pMetadata->xmlKeyValuePairs = 'true' === (string) $pElem->attributes()->{'xml-key-value-pairs'};
101392 }
101393
101394 if (isset($pElem->attributes()->{'max-depth'})) {
101395 $pMetadata->maxDepth = (int) $pElem->attributes()->{'max-depth'};
101396 }
101397
101398 //we need read-only before setter and getter set, because that method depends on flag being set
101399 if (null !== $readOnly = $pElem->attributes()->{'read-only'}) {
101400 $pMetadata->readOnly = 'true' === strtolower($readOnly);
101401 }
101402
101403 $getter = $pElem->attributes()->{'accessor-getter'};
101404 $setter = $pElem->attributes()->{'accessor-setter'};
101405 $pMetadata->setAccessor(
101406 (string) ($pElem->attributes()->{'access-type'} ?: $classAccessType),
101407 $getter ? (string) $getter : null,
101408 $setter ? (string) $setter : null
101409 );
101410
101411 if (null !== $inline = $pElem->attributes()->inline) {
101412 $pMetadata->inline = 'true' === strtolower($inline);
101413 }
101414
101415 }
101416
101417 if ((ExclusionPolicy::NONE === (string)$exclusionPolicy && !$isExclude)
101418 || (ExclusionPolicy::ALL === (string)$exclusionPolicy && $isExpose)) {
101419
101420 $metadata->addPropertyMetadata($pMetadata);
101421 }
101422 }
101423 }
101424
101425 foreach ($elem->xpath('./callback-method') as $method) {
101426 if (!isset($method->attributes()->type)) {
101427 throw new RuntimeException('The type attribute must be set for all callback-method elements.');
101428 }
101429 if (!isset($method->attributes()->name)) {
101430 throw new RuntimeException('The name attribute must be set for all callback-method elements.');
101431 }
101432
101433 switch ((string) $method->attributes()->type) {
101434 case 'pre-serialize':
101435 $metadata->addPreSerializeMethod(new MethodMetadata($name, (string) $method->attributes()->name));
101436 break;
101437
101438 case 'post-serialize':
101439 $metadata->addPostSerializeMethod(new MethodMetadata($name, (string) $method->attributes()->name));
101440 break;
101441
101442 case 'post-deserialize':
101443 $metadata->addPostDeserializeMethod(new MethodMetadata($name, (string) $method->attributes()->name));
101444 break;
101445
101446 case 'handler':
101447 if ( ! isset($method->attributes()->format)) {
101448 throw new RuntimeException('The format attribute must be set for "handler" callback methods.');
101449 }
101450 if ( ! isset($method->attributes()->direction)) {
101451 throw new RuntimeException('The direction attribute must be set for "handler" callback methods.');
101452 }
101453
101454 $direction = GraphNavigator::parseDirection((string) $method->attributes()->direction);
101455 $format = (string) $method->attributes()->format;
101456 $metadata->addHandlerCallback($direction, $format, (string) $method->attributes()->name);
101457
101458 break;
101459
101460 default:
101461 throw new RuntimeException(sprintf('The type "%s" is not supported.', $method->attributes()->name));
101462 }
101463 }
101464
101465 return $metadata;
101466 }
101467
101468 protected function getExtension()
101469 {
101470 return 'xml';
101471 }
101472 }
101473 <?php
101474
101475 /*
101476 * Copyright 2013 Johannes M. Schmitt <schmittjoh@gmail.com>
101477 *
101478 * Licensed under the Apache License, Version 2.0 (the "License");
101479 * you may not use this file except in compliance with the License.
101480 * You may obtain a copy of the License at
101481 *
101482 * http://www.apache.org/licenses/LICENSE-2.0
101483 *
101484 * Unless required by applicable law or agreed to in writing, software
101485 * distributed under the License is distributed on an "AS IS" BASIS,
101486 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
101487 * See the License for the specific language governing permissions and
101488 * limitations under the License.
101489 */
101490
101491 namespace JMS\Serializer\Metadata\Driver;
101492
101493 use JMS\Serializer\Exception\RuntimeException;
101494 use JMS\Serializer\Metadata\ClassMetadata;
101495 use Metadata\Driver\AbstractFileDriver;
101496
101497 class PhpDriver extends AbstractFileDriver
101498 {
101499 protected function loadMetadataFromFile(\ReflectionClass $class, $file)
101500 {
101501 $metadata = require $file;
101502
101503 if (!$metadata instanceof ClassMetadata) {
101504 throw new RuntimeException(sprintf('The file %s was expected to return an instance of ClassMetadata, but returned %s.', $file, json_encode($metadata)));
101505 }
101506 if ($metadata->name !== $class->name) {
101507 throw new RuntimeException(sprintf('The file %s was expected to return metadata for class %s, but instead returned metadata for class %s.', $class->name, $metadata->name));
101508 }
101509
101510 return $metadata;
101511 }
101512
101513 protected function getExtension()
101514 {
101515 return 'php';
101516 }
101517 }
101518 <?php
101519
101520 /*
101521 * Copyright 2013 Johannes M. Schmitt <schmittjoh@gmail.com>
101522 *
101523 * Licensed under the Apache License, Version 2.0 (the "License");
101524 * you may not use this file except in compliance with the License.
101525 * You may obtain a copy of the License at
101526 *
101527 * http://www.apache.org/licenses/LICENSE-2.0
101528 *
101529 * Unless required by applicable law or agreed to in writing, software
101530 * distributed under the License is distributed on an "AS IS" BASIS,
101531 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
101532 * See the License for the specific language governing permissions and
101533 * limitations under the License.
101534 */
101535
101536 namespace JMS\Serializer\Metadata\Driver;
101537
101538 use JMS\Serializer\GraphNavigator;
101539 use JMS\Serializer\Exception\RuntimeException;
101540 use JMS\Serializer\Annotation\ExclusionPolicy;
101541 use Metadata\MethodMetadata;
101542 use JMS\Serializer\Metadata\PropertyMetadata;
101543 use JMS\Serializer\Metadata\VirtualPropertyMetadata;
101544 use JMS\Serializer\Metadata\ClassMetadata;
101545 use Symfony\Component\Yaml\Yaml;
101546 use Metadata\Driver\AbstractFileDriver;
101547
101548 class YamlDriver extends AbstractFileDriver
101549 {
101550 protected function loadMetadataFromFile(\ReflectionClass $class, $file)
101551 {
101552 $config = Yaml::parse(file_get_contents($file));
101553
101554 if (!isset($config[$name = $class->name])) {
101555 throw new RuntimeException(sprintf('Expected metadata for class %s to be defined in %s.', $class->name, $file));
101556 }
101557
101558 $config = $config[$name];
101559 $metadata = new ClassMetadata($name);
101560 $metadata->fileResources[] = $file;
101561 $metadata->fileResources[] = $class->getFileName();
101562 $exclusionPolicy = isset($config['exclusion_policy']) ? strtoupper($config['exclusion_policy']) : 'NONE';
101563 $excludeAll = isset($config['exclude']) ? (Boolean) $config['exclude'] : false;
101564 $classAccessType = isset($config['access_type']) ? $config['access_type'] : PropertyMetadata::ACCESS_TYPE_PROPERTY;
101565
101566 $propertiesMetadata = array();
101567
101568 if (isset($config['accessor_order'])) {
101569 $metadata->setAccessorOrder($config['accessor_order'], isset($config['custom_accessor_order']) ? $config['custom_accessor_order'] : array());
101570 }
101571
101572 if (isset($config['xml_root_name'])) {
101573 $metadata->xmlRootName = (string) $config['xml_root_name'];
101574 }
101575
101576 if (isset($config['discriminator'])) {
101577 if (isset($config['discriminator']['disabled']) && true === $config['discriminator']['disabled']) {
101578 $metadata->discriminatorDisabled = true;
101579 } else {
101580 if ( ! isset($config['discriminator']['field_name'])) {
101581 throw new RuntimeException('The "field_name" attribute must be set for discriminators.');
101582 }
101583
101584 if ( ! isset($config['discriminator']['map']) || ! is_array($config['discriminator']['map'])) {
101585 throw new RuntimeException('The "map" attribute must be set, and be an array for discriminators.');
101586 }
101587
101588 $metadata->setDiscriminator($config['discriminator']['field_name'], $config['discriminator']['map']);
101589 }
101590 }
101591
101592 if (array_key_exists('virtual_properties', $config) ) {
101593
101594 foreach ( $config['virtual_properties'] as $methodName => $propertySettings ) {
101595
101596 if ( !$class->hasMethod( $methodName ) ) {
101597 throw new RuntimeException('The method '.$methodName.' not found in class ' . $class->name);
101598 }
101599
101600 $virtualPropertyMetadata = new VirtualPropertyMetadata( $name, $methodName );
101601
101602 $propertiesMetadata[$methodName] = $virtualPropertyMetadata;
101603 $config['properties'][$methodName] = $propertySettings;
101604 }
101605 }
101606
101607 if (!$excludeAll) {
101608 foreach ($class->getProperties() as $property) {
101609 if ($name !== $property->class) {
101610 continue;
101611 }
101612 $pName = $property->getName();
101613 $propertiesMetadata[$pName] = new PropertyMetadata($name, $pName);
101614 }
101615
101616 foreach ($propertiesMetadata as $pName => $pMetadata) {
101617
101618 $isExclude = false;
101619 $isExpose = $pMetadata instanceof VirtualPropertyMetadata;
101620
101621 if (isset($config['properties'][$pName])) {
101622 $pConfig = $config['properties'][$pName];
101623
101624 if (isset($pConfig['exclude'])) {
101625 $isExclude = (Boolean) $pConfig['exclude'];
101626 }
101627
101628 if (isset($pConfig['expose'])) {
101629 $isExpose = (Boolean) $pConfig['expose'];
101630 }
101631
101632 if (isset($pConfig['since_version'])) {
101633 $pMetadata->sinceVersion = (string) $pConfig['since_version'];
101634 }
101635
101636 if (isset($pConfig['until_version'])) {
101637 $pMetadata->untilVersion = (string) $pConfig['until_version'];
101638 }
101639
101640 if (isset($pConfig['serialized_name'])) {
101641 $pMetadata->serializedName = (string) $pConfig['serialized_name'];
101642 }
101643
101644 if (isset($pConfig['type'])) {
101645 $pMetadata->setType((string) $pConfig['type']);
101646 }
101647
101648 if (isset($pConfig['groups'])) {
101649 $pMetadata->groups = $pConfig['groups'];
101650 }
101651
101652 if (isset($pConfig['xml_list'])) {
101653 $pMetadata->xmlCollection = true;
101654
101655 $colConfig = $pConfig['xml_list'];
101656 if (isset($colConfig['inline'])) {
101657 $pMetadata->xmlCollectionInline = (Boolean) $colConfig['inline'];
101658 }
101659
101660 if (isset($colConfig['entry_name'])) {
101661 $pMetadata->xmlEntryName = (string) $colConfig['entry_name'];
101662 }
101663 }
101664
101665 if (isset($pConfig['xml_map'])) {
101666 $pMetadata->xmlCollection = true;
101667
101668 $colConfig = $pConfig['xml_map'];
101669 if (isset($colConfig['inline'])) {
101670 $pMetadata->xmlCollectionInline = (Boolean) $colConfig['inline'];
101671 }
101672
101673 if (isset($colConfig['entry_name'])) {
101674 $pMetadata->xmlEntryName = (string) $colConfig['entry_name'];
101675 }
101676
101677 if (isset($colConfig['key_attribute_name'])) {
101678 $pMetadata->xmlKeyAttribute = $colConfig['key_attribute_name'];
101679 }
101680 }
101681
101682 if (isset($pConfig['xml_attribute'])) {
101683 $pMetadata->xmlAttribute = (Boolean) $pConfig['xml_attribute'];
101684 }
101685
101686 if (isset($pConfig['xml_attribute_map'])) {
101687 $pMetadata->xmlAttribute = (Boolean) $pConfig['xml_attribute_map'];
101688 }
101689
101690 if (isset($pConfig['xml_value'])) {
101691 $pMetadata->xmlValue = (Boolean) $pConfig['xml_value'];
101692 }
101693
101694 if (isset($pConfig['xml_key_value_pairs'])) {
101695 $pMetadata->xmlKeyValuePairs = (Boolean) $pConfig['xml_key_value_pairs'];
101696 }
101697
101698 //we need read_only before setter and getter set, because that method depends on flag being set
101699 if (isset($pConfig['read_only'])) {
101700 $pMetadata->readOnly = (Boolean) $pConfig['read_only'];
101701 }
101702
101703 $pMetadata->setAccessor(
101704 isset($pConfig['access_type']) ? $pConfig['access_type'] : $classAccessType,
101705 isset($pConfig['accessor']['getter']) ? $pConfig['accessor']['getter'] : null,
101706 isset($pConfig['accessor']['setter']) ? $pConfig['accessor']['setter'] : null
101707 );
101708
101709 if (isset($pConfig['inline'])) {
101710 $pMetadata->inline = (Boolean) $pConfig['inline'];
101711 }
101712
101713 if (isset($pConfig['max_depth'])) {
101714 $pMetadata->maxDepth = (int) $pConfig['max_depth'];
101715 }
101716 }
101717 if ((ExclusionPolicy::NONE === $exclusionPolicy && !$isExclude)
101718 || (ExclusionPolicy::ALL === $exclusionPolicy && $isExpose)) {
101719 $metadata->addPropertyMetadata($pMetadata);
101720 }
101721 }
101722 }
101723
101724 if (isset($config['handler_callbacks'])) {
101725 foreach ($config['handler_callbacks'] as $direction => $formats) {
101726 foreach ($formats as $format => $methodName) {
101727 $direction = GraphNavigator::parseDirection($direction);
101728 $metadata->addHandlerCallback($direction, $format, $methodName);
101729 }
101730 }
101731 }
101732
101733 if (isset($config['callback_methods'])) {
101734 $cConfig = $config['callback_methods'];
101735
101736 if (isset($cConfig['pre_serialize'])) {
101737 $metadata->preSerializeMethods = $this->getCallbackMetadata($class, $cConfig['pre_serialize']);
101738 }
101739 if (isset($cConfig['post_serialize'])) {
101740 $metadata->postSerializeMethods = $this->getCallbackMetadata($class, $cConfig['post_serialize']);
101741 }
101742 if (isset($cConfig['post_deserialize'])) {
101743 $metadata->postDeserializeMethods = $this->getCallbackMetadata($class, $cConfig['post_deserialize']);
101744 }
101745 }
101746
101747 return $metadata;
101748 }
101749
101750 protected function getExtension()
101751 {
101752 return 'yml';
101753 }
101754
101755 private function getCallbackMetadata(\ReflectionClass $class, $config)
101756 {
101757 if (is_string($config)) {
101758 $config = array($config);
101759 } elseif (!is_array($config)) {
101760 throw new RuntimeException(sprintf('callback methods expects a string, or an array of strings that represent method names, but got %s.', json_encode($config['pre_serialize'])));
101761 }
101762
101763 $methods = array();
101764 foreach ($config as $name) {
101765 if (!$class->hasMethod($name)) {
101766 throw new RuntimeException(sprintf('The method %s does not exist in class %s.', $name, $class->name));
101767 }
101768
101769 $methods[] = new MethodMetadata($class->name, $name);
101770 }
101771
101772 return $methods;
101773 }
101774 }
101775 <?php
101776
101777 /*
101778 * Copyright 2013 Johannes M. Schmitt <schmittjoh@gmail.com>
101779 *
101780 * Licensed under the Apache License, Version 2.0 (the "License");
101781 * you may not use this file except in compliance with the License.
101782 * You may obtain a copy of the License at
101783 *
101784 * http://www.apache.org/licenses/LICENSE-2.0
101785 *
101786 * Unless required by applicable law or agreed to in writing, software
101787 * distributed under the License is distributed on an "AS IS" BASIS,
101788 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
101789 * See the License for the specific language governing permissions and
101790 * limitations under the License.
101791 */
101792
101793 namespace JMS\Serializer\Metadata\Driver;
101794
101795 use Doctrine\Common\Persistence\ManagerRegistry;
101796 use JMS\Serializer\Metadata\ClassMetadata;
101797 use JMS\Serializer\Metadata\PropertyMetadata;
101798 use Metadata\Driver\DriverInterface;
101799 use Doctrine\ORM\Mapping\ClassMetadataInfo;
101800
101801 /**
101802 * This class decorates any other driver. If the inner driver does not provide a
101803 * a property type, the decorator will guess based on Doctrine 2 metadata.
101804 */
101805 class DoctrineTypeDriver implements DriverInterface
101806 {
101807 /**
101808 * Map of doctrine 2 field types to JMS\Serializer types
101809 * @var array
101810 */
101811 protected $fieldMapping = array(
101812 'string' => 'string',
101813 'text' => 'string',
101814 'blob' => 'string',
101815
101816 'integer' => 'integer',
101817 'smallint' => 'integer',
101818 'bigint' => 'integer',
101819
101820 'datetime' => 'DateTime',
101821 'datetimetz' => 'DateTime',
101822 'time' => 'DateTime',
101823
101824 'float' => 'float',
101825 'decimal' => 'float',
101826
101827 'boolean' => 'boolean',
101828
101829 'array' => 'array',
101830 'json_array' => 'array',
101831 'simple_array' => 'array<string>',
101832 );
101833
101834 /**
101835 * @var DriverInterface
101836 */
101837 protected $delegate;
101838
101839 /**
101840 * @var ManagerRegistry
101841 */
101842 protected $registry;
101843
101844 public function __construct(DriverInterface $delegate, ManagerRegistry $registry)
101845 {
101846 $this->delegate = $delegate;
101847 $this->registry = $registry;
101848 }
101849
101850 public function loadMetadataForClass(\ReflectionClass $class)
101851 {
101852 /** @var $classMetadata ClassMetadata */
101853 $classMetadata = $this->delegate->loadMetadataForClass($class);
101854
101855 // Abort if the given class is not a mapped entity
101856 if (!$doctrineMetadata = $this->tryLoadingDoctrineMetadata($class->name)) {
101857 return $classMetadata;
101858 }
101859
101860 if ($doctrineMetadata instanceof ClassMetadataInfo) {
101861 if (empty($classMetadata->discriminatorMap) && ! $classMetadata->discriminatorDisabled
101862 && ! empty($doctrineMetadata->discriminatorMap) && $doctrineMetadata->isRootEntity()) {
101863 $classMetadata->setDiscriminator(
101864 $doctrineMetadata->discriminatorColumn['name'],
101865 $doctrineMetadata->discriminatorMap
101866 );
101867 }
101868 }
101869
101870 // We base our scan on the internal driver's property list so that we
101871 // respect any internal white/blacklisting like in the AnnotationDriver
101872 foreach ($classMetadata->propertyMetadata as $propertyMetadata) {
101873 /** @var $propertyMetadata PropertyMetadata */
101874
101875 // If the inner driver provides a type, don't guess anymore.
101876 if ($propertyMetadata->type) {
101877 continue;
101878 }
101879
101880 $propertyName = $propertyMetadata->name;
101881 if ($doctrineMetadata->hasField($propertyName) && $fieldType = $this->normalizeFieldType($doctrineMetadata->getTypeOfField($propertyName))) {
101882 $propertyMetadata->setType($fieldType);
101883 } elseif ($doctrineMetadata->hasAssociation($propertyName)) {
101884 $targetEntity = $doctrineMetadata->getAssociationTargetClass($propertyName);
101885
101886 if (null === $targetMetadata = $this->tryLoadingDoctrineMetadata($targetEntity)) {
101887 continue;
101888 }
101889
101890 // For inheritance schemes, we cannot add any type as we would only add the super-type of the hierarchy.
101891 // On serialization, this would lead to only the supertype being serialized, and properties of subtypes
101892 // being ignored.
101893 if ($targetMetadata instanceof ClassMetadataInfo && ! $targetMetadata->isInheritanceTypeNone()) {
101894 continue;
101895 }
101896
101897 if ($doctrineMetadata->isSingleValuedAssociation($propertyName)) {
101898 $propertyMetadata->setType($targetEntity);
101899 } else {
101900 $propertyMetadata->setType("ArrayCollection<{$targetEntity}>");
101901 }
101902 }
101903 }
101904
101905 return $classMetadata;
101906 }
101907
101908 /**
101909 * @param string $className
101910 *
101911 * @return ClassMetadataInfo|null
101912 */
101913 private function tryLoadingDoctrineMetadata($className) {
101914 if (!$manager = $this->registry->getManagerForClass($className)) {
101915 return null;
101916 }
101917
101918 if ($manager->getMetadataFactory()->isTransient($className)) {
101919 return null;
101920 }
101921
101922 return $manager->getClassMetadata($className);
101923 }
101924
101925 private function normalizeFieldType($type)
101926 {
101927 if (!isset($this->fieldMapping[$type])) {
101928 return;
101929 }
101930
101931 return $this->fieldMapping[$type];
101932 }
101933 }
101934 <?php
101935
101936 /*
101937 * Copyright 2013 Johannes M. Schmitt <schmittjoh@gmail.com>
101938 *
101939 * Licensed under the Apache License, Version 2.0 (the "License");
101940 * you may not use this file except in compliance with the License.
101941 * You may obtain a copy of the License at
101942 *
101943 * http://www.apache.org/licenses/LICENSE-2.0
101944 *
101945 * Unless required by applicable law or agreed to in writing, software
101946 * distributed under the License is distributed on an "AS IS" BASIS,
101947 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
101948 * See the License for the specific language governing permissions and
101949 * limitations under the License.
101950 */
101951
101952 namespace JMS\Serializer\Metadata;
101953
101954 use JMS\Serializer\TypeParser;
101955 use Metadata\PropertyMetadata as BasePropertyMetadata;
101956 use JMS\Serializer\Exception\RuntimeException;
101957
101958 class PropertyMetadata extends BasePropertyMetadata
101959 {
101960 const ACCESS_TYPE_PROPERTY = 'property';
101961 const ACCESS_TYPE_PUBLIC_METHOD = 'public_method';
101962
101963 public $sinceVersion;
101964 public $untilVersion;
101965 public $groups;
101966 public $serializedName;
101967 public $type;
101968 public $xmlCollection = false;
101969 public $xmlCollectionInline = false;
101970 public $xmlEntryName;
101971 public $xmlKeyAttribute;
101972 public $xmlAttribute = false;
101973 public $xmlValue = false;
101974 public $xmlKeyValuePairs = false;
101975 public $getter;
101976 public $setter;
101977 public $inline = false;
101978 public $readOnly = false;
101979 public $xmlAttributeMap = false;
101980 public $maxDepth = null;
101981
101982 private static $typeParser;
101983
101984 public function setAccessor($type, $getter = null, $setter = null)
101985 {
101986 if (self::ACCESS_TYPE_PUBLIC_METHOD === $type) {
101987 $class = $this->reflection->getDeclaringClass();
101988
101989 if (empty($getter)) {
101990 if ($class->hasMethod('get'.$this->name) && $class->getMethod('get'.$this->name)->isPublic()) {
101991 $getter = 'get'.$this->name;
101992 } elseif ($class->hasMethod('is'.$this->name) && $class->getMethod('is'.$this->name)->isPublic()) {
101993 $getter = 'is'.$this->name;
101994 } else {
101995 throw new RuntimeException(sprintf('There is neither a public %s method, nor a public %s method in class %s. Please specify which public method should be used for retrieving the value of the property %s.', 'get'.ucfirst($this->name), 'is'.ucfirst($this->name), $this->class, $this->name));
101996 }
101997 }
101998
101999 if (empty($setter) && !$this->readOnly) {
102000 if ($class->hasMethod('set'.$this->name) && $class->getMethod('set'.$this->name)->isPublic()) {
102001 $setter = 'set'.$this->name;
102002 } else {
102003 throw new RuntimeException(sprintf('There is no public %s method in class %s. Please specify which public method should be used for setting the value of the property %s.', 'set'.ucfirst($this->name), $this->class, $this->name));
102004 }
102005 }
102006 }
102007
102008 $this->getter = $getter;
102009 $this->setter = $setter;
102010 }
102011
102012 public function getValue($obj)
102013 {
102014 if (null === $this->getter) {
102015 return parent::getValue($obj);
102016 }
102017
102018 return $obj->{$this->getter}();
102019 }
102020
102021 public function setType($type)
102022 {
102023 if (null === self::$typeParser) {
102024 self::$typeParser = new TypeParser();
102025 }
102026
102027 $this->type = self::$typeParser->parse($type);
102028 }
102029
102030 public function serialize()
102031 {
102032 return serialize(array(
102033 $this->sinceVersion,
102034 $this->untilVersion,
102035 $this->groups,
102036 $this->serializedName,
102037 $this->type,
102038 $this->xmlCollection,
102039 $this->xmlCollectionInline,
102040 $this->xmlEntryName,
102041 $this->xmlKeyAttribute,
102042 $this->xmlAttribute,
102043 $this->xmlValue,
102044 $this->xmlKeyValuePairs,
102045 $this->getter,
102046 $this->setter,
102047 $this->inline,
102048 $this->readOnly,
102049 $this->xmlAttributeMap,
102050 $this->maxDepth,
102051 parent::serialize(),
102052 ));
102053 }
102054
102055 public function unserialize($str)
102056 {
102057 list(
102058 $this->sinceVersion,
102059 $this->untilVersion,
102060 $this->groups,
102061 $this->serializedName,
102062 $this->type,
102063 $this->xmlCollection,
102064 $this->xmlCollectionInline,
102065 $this->xmlEntryName,
102066 $this->xmlKeyAttribute,
102067 $this->xmlAttribute,
102068 $this->xmlValue,
102069 $this->xmlKeyValuePairs,
102070 $this->getter,
102071 $this->setter,
102072 $this->inline,
102073 $this->readOnly,
102074 $this->xmlAttributeMap,
102075 $this->maxDepth,
102076 $parentStr
102077 ) = unserialize($str);
102078
102079 parent::unserialize($parentStr);
102080 }
102081 }
102082 <?php
102083
102084 /*
102085 * Copyright 2013 Johannes M. Schmitt <schmittjoh@gmail.com>
102086 *
102087 * Licensed under the Apache License, Version 2.0 (the "License");
102088 * you may not use this file except in compliance with the License.
102089 * You may obtain a copy of the License at
102090 *
102091 * http://www.apache.org/licenses/LICENSE-2.0
102092 *
102093 * Unless required by applicable law or agreed to in writing, software
102094 * distributed under the License is distributed on an "AS IS" BASIS,
102095 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
102096 * See the License for the specific language governing permissions and
102097 * limitations under the License.
102098 */
102099
102100 namespace JMS\Serializer\Metadata;
102101
102102 class StaticPropertyMetadata extends PropertyMetadata
102103 {
102104 private $value;
102105
102106 public function __construct($className, $fieldName, $fieldValue)
102107 {
102108 $this->class = $className;
102109 $this->name = $fieldName;
102110 $this->value = $fieldValue;
102111 $this->readOnly = true;
102112 }
102113
102114 public function getValue($obj)
102115 {
102116 return $this->value;
102117 }
102118
102119 public function setValue($obj, $value)
102120 {
102121 throw new \LogicException('StaticPropertyMetadata is immutable.');
102122 }
102123
102124 public function setAccessor($type, $getter = null, $setter = null)
102125 {
102126 }
102127
102128 public function serialize()
102129 {
102130 return serialize(array(
102131 $this->sinceVersion,
102132 $this->untilVersion,
102133 $this->groups,
102134 $this->serializedName,
102135 $this->type,
102136 $this->xmlCollection,
102137 $this->xmlCollectionInline,
102138 $this->xmlEntryName,
102139 $this->xmlKeyAttribute,
102140 $this->xmlAttribute,
102141 $this->xmlValue,
102142 $this->xmlKeyValuePairs,
102143 $this->getter,
102144 $this->setter,
102145 $this->inline,
102146 $this->readOnly,
102147 $this->class,
102148 $this->name,
102149 $this->value
102150 ));
102151 }
102152
102153 public function unserialize($str)
102154 {
102155 list(
102156 $this->sinceVersion,
102157 $this->untilVersion,
102158 $this->groups,
102159 $this->serializedName,
102160 $this->type,
102161 $this->xmlCollection,
102162 $this->xmlCollectionInline,
102163 $this->xmlEntryName,
102164 $this->xmlKeyAttribute,
102165 $this->xmlAttribute,
102166 $this->xmlValue,
102167 $this->xmlKeyValuePairs,
102168 $this->getter,
102169 $this->setter,
102170 $this->inline,
102171 $this->readOnly,
102172 $this->class,
102173 $this->name,
102174 $this->value
102175 ) = unserialize($str);
102176 }
102177 }<?php
102178
102179 /*
102180 * Copyright 2013 Johannes M. Schmitt <schmittjoh@gmail.com>
102181 *
102182 * Licensed under the Apache License, Version 2.0 (the "License");
102183 * you may not use this file except in compliance with the License.
102184 * You may obtain a copy of the License at
102185 *
102186 * http://www.apache.org/licenses/LICENSE-2.0
102187 *
102188 * Unless required by applicable law or agreed to in writing, software
102189 * distributed under the License is distributed on an "AS IS" BASIS,
102190 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
102191 * See the License for the specific language governing permissions and
102192 * limitations under the License.
102193 */
102194
102195 namespace JMS\Serializer\Metadata;
102196
102197 use JMS\Serializer\Exception\InvalidArgumentException;
102198 use Metadata\MergeableInterface;
102199 use Metadata\MethodMetadata;
102200 use Metadata\MergeableClassMetadata;
102201 use Metadata\PropertyMetadata as BasePropertyMetadata;
102202
102203 /**
102204 * Class Metadata used to customize the serialization process.
102205 *
102206 * @author Johannes M. Schmitt <schmittjoh@gmail.com>
102207 */
102208 class ClassMetadata extends MergeableClassMetadata
102209 {
102210 const ACCESSOR_ORDER_UNDEFINED = 'undefined';
102211 const ACCESSOR_ORDER_ALPHABETICAL = 'alphabetical';
102212 const ACCESSOR_ORDER_CUSTOM = 'custom';
102213
102214 /** @var \ReflectionMethod[] */
102215 public $preSerializeMethods = array();
102216
102217 /** @var \ReflectionMethod[] */
102218 public $postSerializeMethods = array();
102219
102220 /** @var \ReflectionMethod[] */
102221 public $postDeserializeMethods = array();
102222
102223 public $xmlRootName;
102224 public $accessorOrder;
102225 public $customOrder;
102226 public $handlerCallbacks = array();
102227
102228 public $discriminatorDisabled = false;
102229 public $discriminatorBaseClass;
102230 public $discriminatorFieldName;
102231 public $discriminatorValue;
102232 public $discriminatorMap = array();
102233
102234 public function setDiscriminator($fieldName, array $map)
102235 {
102236 if (empty($fieldName)) {
102237 throw new \InvalidArgumentException('The $fieldName cannot be empty.');
102238 }
102239
102240 if (empty($map)) {
102241 throw new \InvalidArgumentException('The discriminator map cannot be empty.');
102242 }
102243
102244 $this->discriminatorBaseClass = $this->name;
102245 $this->discriminatorFieldName = $fieldName;
102246 $this->discriminatorMap = $map;
102247 }
102248
102249 /**
102250 * Sets the order of properties in the class.
102251 *
102252 * @param string $order
102253 * @param array $customOrder
102254 *
102255 * @throws InvalidArgumentException When the accessor order is not valid
102256 * @throws InvalidArgumentException When the custom order is not valid
102257 */
102258 public function setAccessorOrder($order, array $customOrder = array())
102259 {
102260 if (!in_array($order, array(self::ACCESSOR_ORDER_UNDEFINED, self::ACCESSOR_ORDER_ALPHABETICAL, self::ACCESSOR_ORDER_CUSTOM), true)) {
102261 throw new InvalidArgumentException(sprintf('The accessor order "%s" is invalid.', $order));
102262 }
102263
102264 foreach ($customOrder as $name) {
102265 if (!is_string($name)) {
102266 throw new InvalidArgumentException(sprintf('$customOrder is expected to be a list of strings, but got element of value %s.', json_encode($name)));
102267 }
102268 }
102269
102270 $this->accessorOrder = $order;
102271 $this->customOrder = array_flip($customOrder);
102272 $this->sortProperties();
102273 }
102274
102275 public function addPropertyMetadata(BasePropertyMetadata $metadata)
102276 {
102277 parent::addPropertyMetadata($metadata);
102278 $this->sortProperties();
102279 }
102280
102281 public function addPreSerializeMethod(MethodMetadata $method)
102282 {
102283 $this->preSerializeMethods[] = $method;
102284 }
102285
102286 public function addPostSerializeMethod(MethodMetadata $method)
102287 {
102288 $this->postSerializeMethods[] = $method;
102289 }
102290
102291 public function addPostDeserializeMethod(MethodMetadata $method)
102292 {
102293 $this->postDeserializeMethods[] = $method;
102294 }
102295
102296 /**
102297 * @param integer $direction
102298 * @param string|integer $format
102299 * @param string $methodName
102300 */
102301 public function addHandlerCallback($direction, $format, $methodName)
102302 {
102303 $this->handlerCallbacks[$direction][$format] = $methodName;
102304 }
102305
102306 public function merge(MergeableInterface $object)
102307 {
102308 if ( ! $object instanceof ClassMetadata) {
102309 throw new InvalidArgumentException('$object must be an instance of ClassMetadata.');
102310 }
102311 parent::merge($object);
102312
102313 $this->preSerializeMethods = array_merge($this->preSerializeMethods, $object->preSerializeMethods);
102314 $this->postSerializeMethods = array_merge($this->postSerializeMethods, $object->postSerializeMethods);
102315 $this->postDeserializeMethods = array_merge($this->postDeserializeMethods, $object->postDeserializeMethods);
102316 $this->xmlRootName = $object->xmlRootName;
102317
102318 // Handler methods are taken from the outer class completely.
102319 $this->handlerCallbacks = $object->handlerCallbacks;
102320
102321 if ($object->accessorOrder) {
102322 $this->accessorOrder = $object->accessorOrder;
102323 $this->customOrder = $object->customOrder;
102324 }
102325
102326 if ($object->discriminatorFieldName && $this->discriminatorFieldName) {
102327 throw new \LogicException(sprintf(
102328 'The discriminator of class "%s" would overwrite the discriminator of the parent class "%s". Please define all possible sub-classes in the discriminator of %s.',
102329 $object->name,
102330 $this->discriminatorBaseClass,
102331 $this->discriminatorBaseClass
102332 ));
102333 }
102334
102335 if ($this->discriminatorMap && ! $this->reflection->isAbstract()) {
102336 if (false === $typeValue = array_search($this->name, $this->discriminatorMap, true)) {
102337 throw new \LogicException(sprintf(
102338 'The sub-class "%s" is not listed in the discriminator of the base class "%s".',
102339 $this->name,
102340 $this->discriminatorBaseClass
102341 ));
102342 }
102343
102344 $this->discriminatorValue = $typeValue;
102345
102346 if (isset($this->propertyMetadata[$this->discriminatorFieldName])
102347 && ! $this->propertyMetadata[$this->discriminatorFieldName] instanceof StaticPropertyMetadata) {
102348 throw new \LogicException(sprintf(
102349 'The discriminator field name "%s" of the base-class "%s" conflicts with a regular property of the sub-class "%s".',
102350 $this->discriminatorFieldName,
102351 $this->discriminatorBaseClass,
102352 $this->name
102353 ));
102354 }
102355
102356 $discriminatorProperty = new StaticPropertyMetadata(
102357 $this->name,
102358 $this->discriminatorFieldName,
102359 $typeValue
102360 );
102361 $discriminatorProperty->serializedName = $this->discriminatorFieldName;
102362 $this->propertyMetadata[$this->discriminatorFieldName] = $discriminatorProperty;
102363 }
102364
102365 $this->sortProperties();
102366 }
102367
102368 public function serialize()
102369 {
102370 $this->sortProperties();
102371
102372 return serialize(array(
102373 $this->preSerializeMethods,
102374 $this->postSerializeMethods,
102375 $this->postDeserializeMethods,
102376 $this->xmlRootName,
102377 $this->accessorOrder,
102378 $this->customOrder,
102379 $this->handlerCallbacks,
102380 $this->discriminatorDisabled,
102381 $this->discriminatorBaseClass,
102382 $this->discriminatorFieldName,
102383 $this->discriminatorValue,
102384 $this->discriminatorMap,
102385 parent::serialize(),
102386 ));
102387 }
102388
102389 public function unserialize($str)
102390 {
102391 list(
102392 $this->preSerializeMethods,
102393 $this->postSerializeMethods,
102394 $this->postDeserializeMethods,
102395 $this->xmlRootName,
102396 $this->accessorOrder,
102397 $this->customOrder,
102398 $this->handlerCallbacks,
102399 $this->discriminatorDisabled,
102400 $this->discriminatorBaseClass,
102401 $this->discriminatorFieldName,
102402 $this->discriminatorValue,
102403 $this->discriminatorMap,
102404 $parentStr
102405 ) = unserialize($str);
102406
102407 parent::unserialize($parentStr);
102408 }
102409
102410 private function sortProperties()
102411 {
102412 switch ($this->accessorOrder) {
102413 case self::ACCESSOR_ORDER_ALPHABETICAL:
102414 ksort($this->propertyMetadata);
102415 break;
102416
102417 case self::ACCESSOR_ORDER_CUSTOM:
102418 $order = $this->customOrder;
102419 uksort($this->propertyMetadata, function($a, $b) use ($order) {
102420 $existsA = isset($order[$a]);
102421 $existsB = isset($order[$b]);
102422
102423 if (!$existsA && !$existsB) {
102424 return 0;
102425 }
102426
102427 if (!$existsA) {
102428 return 1;
102429 }
102430
102431 if (!$existsB) {
102432 return -1;
102433 }
102434
102435 return $order[$a] < $order[$b] ? -1 : 1;
102436 });
102437 break;
102438 }
102439 }
102440 }
102441 <?php
102442
102443 /*
102444 * Copyright 2013 Johannes M. Schmitt <schmittjoh@gmail.com>
102445 *
102446 * Licensed under the Apache License, Version 2.0 (the "License");
102447 * you may not use this file except in compliance with the License.
102448 * You may obtain a copy of the License at
102449 *
102450 * http://www.apache.org/licenses/LICENSE-2.0
102451 *
102452 * Unless required by applicable law or agreed to in writing, software
102453 * distributed under the License is distributed on an "AS IS" BASIS,
102454 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
102455 * See the License for the specific language governing permissions and
102456 * limitations under the License.
102457 */
102458
102459 namespace JMS\Serializer;
102460
102461 use JMS\Serializer\Exception\RuntimeException;
102462
102463 class JsonDeserializationVisitor extends GenericDeserializationVisitor
102464 {
102465 protected function decode($str)
102466 {
102467 $decoded = json_decode($str, true);
102468
102469 switch (json_last_error()) {
102470 case JSON_ERROR_NONE:
102471 return $decoded;
102472
102473 case JSON_ERROR_DEPTH:
102474 throw new RuntimeException('Could not decode JSON, maximum stack depth exceeded.');
102475
102476 case JSON_ERROR_STATE_MISMATCH:
102477 throw new RuntimeException('Could not decode JSON, underflow or the nodes mismatch.');
102478
102479 case JSON_ERROR_CTRL_CHAR:
102480 throw new RuntimeException('Could not decode JSON, unexpected control character found.');
102481
102482 case JSON_ERROR_SYNTAX:
102483 throw new RuntimeException('Could not decode JSON, syntax error - malformed JSON.');
102484
102485 case JSON_ERROR_UTF8:
102486 throw new RuntimeException('Could not decode JSON, malformed UTF-8 characters (incorrectly encoded?)');
102487
102488 default:
102489 throw new RuntimeException('Could not decode JSON.');
102490 }
102491 }
102492 }
102493 <?php
102494
102495 /*
102496 * Copyright 2013 Johannes M. Schmitt <schmittjoh@gmail.com>
102497 *
102498 * Licensed under the Apache License, Version 2.0 (the "License");
102499 * you may not use this file except in compliance with the License.
102500 * You may obtain a copy of the License at
102501 *
102502 * http://www.apache.org/licenses/LICENSE-2.0
102503 *
102504 * Unless required by applicable law or agreed to in writing, software
102505 * distributed under the License is distributed on an "AS IS" BASIS,
102506 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
102507 * See the License for the specific language governing permissions and
102508 * limitations under the License.
102509 */
102510
102511 namespace JMS\Serializer;
102512
102513 use JMS\Serializer\Handler\PhpCollectionHandler;
102514 use JMS\Serializer\Exception\RuntimeException;
102515 use Metadata\MetadataFactory;
102516 use JMS\Serializer\Metadata\Driver\AnnotationDriver;
102517 use JMS\Serializer\Handler\HandlerRegistry;
102518 use JMS\Serializer\Construction\UnserializeObjectConstructor;
102519 use PhpCollection\Map;
102520 use JMS\Serializer\EventDispatcher\EventDispatcher;
102521 use Metadata\Driver\DriverChain;
102522 use JMS\Serializer\Metadata\Driver\YamlDriver;
102523 use JMS\Serializer\Metadata\Driver\XmlDriver;
102524 use Metadata\Driver\FileLocator;
102525 use JMS\Serializer\Handler\DateHandler;
102526 use JMS\Serializer\Handler\ArrayCollectionHandler;
102527 use JMS\Serializer\Construction\ObjectConstructorInterface;
102528 use JMS\Serializer\EventDispatcher\Subscriber\DoctrineProxySubscriber;
102529 use JMS\Serializer\Naming\CamelCaseNamingStrategy;
102530 use JMS\Serializer\Naming\PropertyNamingStrategyInterface;
102531 use Doctrine\Common\Annotations\Reader;
102532 use Doctrine\Common\Annotations\AnnotationReader;
102533 use Doctrine\Common\Annotations\FileCacheReader;
102534 use Metadata\Cache\FileCache;
102535 use JMS\Serializer\Naming\SerializedNameAnnotationStrategy;
102536 use JMS\Serializer\Exception\InvalidArgumentException;
102537
102538 /**
102539 * Builder for serializer instances.
102540 *
102541 * This object makes serializer construction a breeze for projects that do not use
102542 * any special dependency injection container.
102543 *
102544 * @author Johannes M. Schmitt <schmittjoh@gmail.com>
102545 */
102546 class SerializerBuilder
102547 {
102548 private $metadataDirs = array();
102549 private $handlerRegistry;
102550 private $handlersConfigured = false;
102551 private $eventDispatcher;
102552 private $listenersConfigured = false;
102553 private $objectConstructor;
102554 private $serializationVisitors;
102555 private $deserializationVisitors;
102556 private $visitorsAdded = false;
102557 private $propertyNamingStrategy;
102558 private $debug = false;
102559 private $cacheDir;
102560 private $annotationReader;
102561 private $includeInterfaceMetadata = false;
102562
102563 public static function create()
102564 {
102565 return new static();
102566 }
102567
102568 public function __construct()
102569 {
102570 $this->handlerRegistry = new HandlerRegistry();
102571 $this->eventDispatcher = new EventDispatcher();
102572 $this->serializationVisitors = new Map();
102573 $this->deserializationVisitors = new Map();
102574 }
102575
102576 public function setAnnotationReader(Reader $reader)
102577 {
102578 $this->annotationReader = $reader;
102579
102580 return $this;
102581 }
102582
102583 public function setDebug($bool)
102584 {
102585 $this->debug = (boolean) $bool;
102586
102587 return $this;
102588 }
102589
102590 public function setCacheDir($dir)
102591 {
102592 if ( ! is_dir($dir)) {
102593 $this->createDir($dir);
102594 }
102595 if ( ! is_writable($dir)) {
102596 throw new InvalidArgumentException(sprintf('The cache directory "%s" is not writable.', $dir));
102597 }
102598
102599 $this->cacheDir = $dir;
102600
102601 return $this;
102602 }
102603
102604 public function addDefaultHandlers()
102605 {
102606 $this->handlersConfigured = true;
102607 $this->handlerRegistry->registerSubscribingHandler(new DateHandler());
102608 $this->handlerRegistry->registerSubscribingHandler(new PhpCollectionHandler());
102609 $this->handlerRegistry->registerSubscribingHandler(new ArrayCollectionHandler());
102610
102611 return $this;
102612 }
102613
102614 public function configureHandlers(\Closure $closure)
102615 {
102616 $this->handlersConfigured = true;
102617 $closure($this->handlerRegistry);
102618
102619 return $this;
102620 }
102621
102622 public function addDefaultListeners()
102623 {
102624 $this->listenersConfigured = true;
102625 $this->eventDispatcher->addSubscriber(new DoctrineProxySubscriber());
102626
102627 return $this;
102628 }
102629
102630 public function configureListeners(\Closure $closure)
102631 {
102632 $this->listenersConfigured = true;
102633 $closure($this->eventDispatcher);
102634
102635 return $this;
102636 }
102637
102638 public function setObjectConstructor(ObjectConstructorInterface $constructor)
102639 {
102640 $this->objectConstructor = $constructor;
102641
102642 return $this;
102643 }
102644
102645 public function setPropertyNamingStrategy(PropertyNamingStrategyInterface $propertyNamingStrategy)
102646 {
102647 $this->propertyNamingStrategy = $propertyNamingStrategy;
102648
102649 return $this;
102650 }
102651
102652 public function setSerializationVisitor($format, VisitorInterface $visitor)
102653 {
102654 $this->visitorsAdded = true;
102655 $this->serializationVisitors->set($format, $visitor);
102656
102657 return $this;
102658 }
102659
102660 public function setDeserializationVisitor($format, VisitorInterface $visitor)
102661 {
102662 $this->visitorsAdded = true;
102663 $this->deserializationVisitors->set($format, $visitor);
102664
102665 return $this;
102666 }
102667
102668 public function addDefaultSerializationVisitors()
102669 {
102670 $this->initializePropertyNamingStrategy();
102671
102672 $this->visitorsAdded = true;
102673 $this->serializationVisitors->setAll(array(
102674 'xml' => new XmlSerializationVisitor($this->propertyNamingStrategy),
102675 'yml' => new YamlSerializationVisitor($this->propertyNamingStrategy),
102676 'json' => new JsonSerializationVisitor($this->propertyNamingStrategy),
102677 ));
102678
102679 return $this;
102680 }
102681
102682 public function addDefaultDeserializationVisitors()
102683 {
102684 $this->initializePropertyNamingStrategy();
102685
102686 $this->visitorsAdded = true;
102687 $this->deserializationVisitors->setAll(array(
102688 'xml' => new XmlDeserializationVisitor($this->propertyNamingStrategy),
102689 'json' => new JsonDeserializationVisitor($this->propertyNamingStrategy),
102690 ));
102691
102692 return $this;
102693 }
102694
102695 /**
102696 * @param Boolean $include Whether to include the metadata from the interfaces
102697 *
102698 * @return SerializerBuilder
102699 */
102700 public function includeInterfaceMetadata($include)
102701 {
102702 $this->includeInterfaceMetadata = (Boolean) $include;
102703
102704 return $this;
102705 }
102706
102707 /**
102708 * Sets a map of namespace prefixes to directories.
102709 *
102710 * This method overrides any previously defined directories.
102711 *
102712 * @param array<string,string> $namespacePrefixToDirMap
102713 *
102714 * @return SerializerBuilder
102715 *
102716 * @throws InvalidArgumentException When a directory does not exist
102717 */
102718 public function setMetadataDirs(array $namespacePrefixToDirMap)
102719 {
102720 foreach ($namespacePrefixToDirMap as $dir) {
102721 if ( ! is_dir($dir)) {
102722 throw new InvalidArgumentException(sprintf('The directory "%s" does not exist.', $dir));
102723 }
102724 }
102725
102726 $this->metadataDirs = $namespacePrefixToDirMap;
102727
102728 return $this;
102729 }
102730
102731 /**
102732 * Adds a directory where the serializer will look for class metadata.
102733 *
102734 * The namespace prefix will make the names of the actual metadata files a bit shorter. For example, let's assume
102735 * that you have a directory where you only store metadata files for the ``MyApplication\Entity`` namespace.
102736 *
102737 * If you use an empty prefix, your metadata files would need to look like:
102738 *
102739 * ``my-dir/MyApplication.Entity.SomeObject.yml``
102740 * ``my-dir/MyApplication.Entity.OtherObject.xml``
102741 *
102742 * If you use ``MyApplication\Entity`` as prefix, your metadata files would need to look like:
102743 *
102744 * ``my-dir/SomeObject.yml``
102745 * ``my-dir/OtherObject.yml``
102746 *
102747 * Please keep in mind that you currently may only have one directory per namespace prefix.
102748 *
102749 * @param string $dir The directory where metadata files are located.
102750 * @param string $namespacePrefix An optional prefix if you only store metadata for specific namespaces in this directory.
102751 *
102752 * @return SerializerBuilder
102753 *
102754 * @throws InvalidArgumentException When a directory does not exist
102755 * @throws InvalidArgumentException When a directory has already been registered
102756 */
102757 public function addMetadataDir($dir, $namespacePrefix = '')
102758 {
102759 if ( ! is_dir($dir)) {
102760 throw new InvalidArgumentException(sprintf('The directory "%s" does not exist.', $dir));
102761 }
102762
102763 if (isset($this->metadataDirs[$namespacePrefix])) {
102764 throw new InvalidArgumentException(sprintf('There is already a directory configured for the namespace prefix "%s". Please use replaceMetadataDir() to override directories.', $namespacePrefix));
102765 }
102766
102767 $this->metadataDirs[$namespacePrefix] = $dir;
102768
102769 return $this;
102770 }
102771
102772 /**
102773 * Adds a map of namespace prefixes to directories.
102774 *
102775 * @param array<string,string> $namespacePrefixToDirMap
102776 *
102777 * @return SerializerBuilder
102778 */
102779 public function addMetadataDirs(array $namespacePrefixToDirMap)
102780 {
102781 foreach ($namespacePrefixToDirMap as $prefix => $dir) {
102782 $this->addMetadataDir($dir, $prefix);
102783 }
102784
102785 return $this;
102786 }
102787
102788 /**
102789 * Similar to addMetadataDir(), but overrides an existing entry.
102790 *
102791 * @param string $dir
102792 * @param string $namespacePrefix
102793 *
102794 * @return SerializerBuilder
102795 *
102796 * @throws InvalidArgumentException When a directory does not exist
102797 * @throws InvalidArgumentException When no directory is configured for the ns prefix
102798 */
102799 public function replaceMetadataDir($dir, $namespacePrefix = '')
102800 {
102801 if ( ! is_dir($dir)) {
102802 throw new InvalidArgumentException(sprintf('The directory "%s" does not exist.', $dir));
102803 }
102804
102805 if ( ! isset($this->metadataDirs[$namespacePrefix])) {
102806 throw new InvalidArgumentException(sprintf('There is no directory configured for namespace prefix "%s". Please use addMetadataDir() for adding new directories.', $namespacePrefix));
102807 }
102808
102809 $this->metadataDirs[$namespacePrefix] = $dir;
102810
102811 return $this;
102812 }
102813
102814 public function build()
102815 {
102816 $annotationReader = $this->annotationReader;
102817 if (null === $annotationReader) {
102818 $annotationReader = new AnnotationReader();
102819
102820 if (null !== $this->cacheDir) {
102821 $this->createDir($this->cacheDir.'/annotations');
102822 $annotationReader = new FileCacheReader($annotationReader, $this->cacheDir.'/annotations', $this->debug);
102823 }
102824 }
102825
102826 if ( ! empty($this->metadataDirs)) {
102827 $fileLocator = new FileLocator($this->metadataDirs);
102828 $metadataDriver = new DriverChain(array(
102829 new YamlDriver($fileLocator),
102830 new XmlDriver($fileLocator),
102831 new AnnotationDriver($annotationReader),
102832 ));
102833 } else {
102834 $metadataDriver = new AnnotationDriver($annotationReader);
102835 }
102836
102837 $metadataFactory = new MetadataFactory($metadataDriver, null, $this->debug);
102838
102839 $metadataFactory->setIncludeInterfaces($this->includeInterfaceMetadata);
102840
102841 if (null !== $this->cacheDir) {
102842 $this->createDir($this->cacheDir.'/metadata');
102843 $metadataFactory->setCache(new FileCache($this->cacheDir.'/metadata'));
102844 }
102845
102846 if ( ! $this->handlersConfigured) {
102847 $this->addDefaultHandlers();
102848 }
102849
102850 if ( ! $this->listenersConfigured) {
102851 $this->addDefaultListeners();
102852 }
102853
102854 if ( ! $this->visitorsAdded) {
102855 $this->addDefaultSerializationVisitors();
102856 $this->addDefaultDeserializationVisitors();
102857 }
102858
102859 return new Serializer(
102860 $metadataFactory,
102861 $this->handlerRegistry,
102862 $this->objectConstructor ?: new UnserializeObjectConstructor(),
102863 $this->serializationVisitors,
102864 $this->deserializationVisitors,
102865 $this->eventDispatcher
102866 );
102867 }
102868
102869 private function initializePropertyNamingStrategy()
102870 {
102871 if (null !== $this->propertyNamingStrategy) {
102872 return;
102873 }
102874
102875 $this->propertyNamingStrategy = new SerializedNameAnnotationStrategy(new CamelCaseNamingStrategy());
102876 }
102877
102878 private function createDir($dir)
102879 {
102880 if (is_dir($dir)) {
102881 return;
102882 }
102883
102884 if (false === @mkdir($dir, 0777, true)) {
102885 throw new RuntimeException(sprintf('Could not create directory "%s".', $dir));
102886 }
102887 }
102888 }
102889 {
102890 "hash": "064fb78d8b7dfa1ee7477308d1c6f2dd",
102891 "packages": [
102892 {
102893 "name": "doctrine/annotations",
102894 "version": "v1.1.1",
102895 "source": {
102896 "type": "git",
102897 "url": "https://github.com/doctrine/annotations.git",
102898 "reference": "v1.1.1"
102899 },
102900 "dist": {
102901 "type": "zip",
102902 "url": "https://api.github.com/repos/doctrine/annotations/zipball/v1.1.1",
102903 "reference": "v1.1.1",
102904 "shasum": ""
102905 },
102906 "require": {
102907 "doctrine/lexer": "1.*",
102908 "php": ">=5.3.2"
102909 },
102910 "require-dev": {
102911 "doctrine/cache": "1.*"
102912 },
102913 "type": "library",
102914 "extra": {
102915 "branch-alias": {
102916 "dev-master": "1.0.x-dev"
102917 }
102918 },
102919 "autoload": {
102920 "psr-0": {
102921 "Doctrine\\Common\\Annotations\\": "lib/"
102922 }
102923 },
102924 "notification-url": "https://packagist.org/downloads/",
102925 "license": [
102926 "MIT"
102927 ],
102928 "authors": [
102929 {
102930 "name": "Jonathan Wage",
102931 "email": "jonwage@gmail.com",
102932 "homepage": "http://www.jwage.com/"
102933 },
102934 {
102935 "name": "Guilherme Blanco",
102936 "email": "guilhermeblanco@gmail.com",
102937 "homepage": "http://www.instaclick.com"
102938 },
102939 {
102940 "name": "Roman Borschel",
102941 "email": "roman@code-factory.org"
102942 },
102943 {
102944 "name": "Benjamin Eberlei",
102945 "email": "kontakt@beberlei.de"
102946 },
102947 {
102948 "name": "Johannes Schmitt",
102949 "email": "schmittjoh@gmail.com",
102950 "homepage": "https://github.com/schmittjoh",
102951 "role": "Developer of wrapped JMSSerializerBundle"
102952 }
102953 ],
102954 "description": "Docblock Annotations Parser",
102955 "homepage": "http://www.doctrine-project.org",
102956 "keywords": [
102957 "annotations",
102958 "docblock",
102959 "parser"
102960 ],
102961 "time": "2013-04-20 08:30:17"
102962 },
102963 {
102964 "name": "doctrine/lexer",
102965 "version": "dev-master",
102966 "source": {
102967 "type": "git",
102968 "url": "https://github.com/doctrine/lexer.git",
102969 "reference": "bc0e1f0cc285127a38c6c8ea88bc5dba2fd53e94"
102970 },
102971 "dist": {
102972 "type": "zip",
102973 "url": "https://api.github.com/repos/doctrine/lexer/zipball/bc0e1f0cc285127a38c6c8ea88bc5dba2fd53e94",
102974 "reference": "bc0e1f0cc285127a38c6c8ea88bc5dba2fd53e94",
102975 "shasum": ""
102976 },
102977 "require": {
102978 "php": ">=5.3.2"
102979 },
102980 "type": "library",
102981 "extra": {
102982 "branch-alias": {
102983 "dev-master": "1.0.x-dev"
102984 }
102985 },
102986 "autoload": {
102987 "psr-0": {
102988 "Doctrine\\Common\\Lexer\\": "lib/"
102989 }
102990 },
102991 "notification-url": "https://packagist.org/downloads/",
102992 "license": [
102993 "MIT"
102994 ],
102995 "authors": [
102996 {
102997 "name": "Guilherme Blanco",
102998 "email": "guilhermeblanco@gmail.com",
102999 "homepage": "http://www.instaclick.com"
103000 },
103001 {
103002 "name": "Roman Borschel",
103003 "email": "roman@code-factory.org"
103004 },
103005 {
103006 "name": "Johannes Schmitt",
103007 "email": "schmittjoh@gmail.com",
103008 "homepage": "https://github.com/schmittjoh",
103009 "role": "Developer of wrapped JMSSerializerBundle"
103010 }
103011 ],
103012 "description": "Base library for a lexer that can be used in Top-Down, Recursive Descent Parsers.",
103013 "homepage": "http://www.doctrine-project.org",
103014 "keywords": [
103015 "lexer",
103016 "parser"
103017 ],
103018 "time": "2013-03-07 12:15:25"
103019 },
103020 {
103021 "name": "jms/metadata",
103022 "version": "dev-master",
103023 "source": {
103024 "type": "git",
103025 "url": "https://github.com/schmittjoh/metadata.git",
103026 "reference": "0979c7d9eb7f0031a3c5ffed1d6e1fa4eb846e4e"
103027 },
103028 "dist": {
103029 "type": "zip",
103030 "url": "https://api.github.com/repos/schmittjoh/metadata/zipball/0979c7d9eb7f0031a3c5ffed1d6e1fa4eb846e4e",
103031 "reference": "0979c7d9eb7f0031a3c5ffed1d6e1fa4eb846e4e",
103032 "shasum": ""
103033 },
103034 "require": {
103035 "php": ">=5.3.0"
103036 },
103037 "require-dev": {
103038 "doctrine/common": ">=2.0,<2.4-dev"
103039 },
103040 "type": "library",
103041 "extra": {
103042 "branch-alias": {
103043 "dev-master": "1.4.x-dev"
103044 }
103045 },
103046 "autoload": {
103047 "psr-0": {
103048 "Metadata\\": "src/"
103049 }
103050 },
103051 "notification-url": "https://packagist.org/downloads/",
103052 "license": [
103053 "Apache"
103054 ],
103055 "authors": [
103056 {
103057 "name": "Johannes M. Schmitt",
103058 "email": "schmittjoh@gmail.com",
103059 "homepage": "http://jmsyst.com",
103060 "role": "Developer of wrapped JMSSerializerBundle"
103061 }
103062 ],
103063 "description": "Class/method/property metadata management in PHP",
103064 "keywords": [
103065 "annotations",
103066 "metadata",
103067 "xml",
103068 "yaml"
103069 ],
103070 "time": "2013-03-28 16:32:37"
103071 },
103072 {
103073 "name": "jms/parser-lib",
103074 "version": "dev-master",
103075 "source": {
103076 "type": "git",
103077 "url": "https://github.com/schmittjoh/parser-lib.git",
103078 "reference": "4d469a70c6dd03f921cbdeadafbcb261bb23e8b0"
103079 },
103080 "dist": {
103081 "type": "zip",
103082 "url": "https://api.github.com/repos/schmittjoh/parser-lib/zipball/4d469a70c6dd03f921cbdeadafbcb261bb23e8b0",
103083 "reference": "4d469a70c6dd03f921cbdeadafbcb261bb23e8b0",
103084 "shasum": ""
103085 },
103086 "require": {
103087 "phpoption/phpoption": ">=0.9,<2.0-dev"
103088 },
103089 "type": "library",
103090 "extra": {
103091 "branch-alias": {
103092 "dev-master": "1.1-dev"
103093 }
103094 },
103095 "autoload": {
103096 "psr-0": {
103097 "JMS\\": "src/"
103098 }
103099 },
103100 "notification-url": "https://packagist.org/downloads/",
103101 "license": [
103102 "Apache2"
103103 ],
103104 "description": "A library for easily creating recursive-descent parsers.",
103105 "time": "2012-11-30 08:11:04"
103106 },
103107 {
103108 "name": "phpcollection/phpcollection",
103109 "version": "dev-master",
103110 "source": {
103111 "type": "git",
103112 "url": "https://github.com/schmittjoh/php-collection.git",
103113 "reference": "3ce8ffb990e9511c7a843f20740373eb27e4c3bd"
103114 },
103115 "dist": {
103116 "type": "zip",
103117 "url": "https://api.github.com/repos/schmittjoh/php-collection/zipball/3ce8ffb990e9511c7a843f20740373eb27e4c3bd",
103118 "reference": "3ce8ffb990e9511c7a843f20740373eb27e4c3bd",
103119 "shasum": ""
103120 },
103121 "require": {
103122 "phpoption/phpoption": "1.*"
103123 },
103124 "type": "library",
103125 "extra": {
103126 "branch-alias": {
103127 "dev-master": "0.2-dev"
103128 }
103129 },
103130 "autoload": {
103131 "psr-0": {
103132 "PhpCollection": "src/"
103133 }
103134 },
103135 "notification-url": "https://packagist.org/downloads/",
103136 "license": [
103137 "Apache2"
103138 ],
103139 "authors": [
103140 {
103141 "name": "Johannes M. Schmitt",
103142 "email": "schmittjoh@gmail.com",
103143 "homepage": "https://github.com/schmittjoh",
103144 "role": "Developer of wrapped JMSSerializerBundle"
103145 }
103146 ],
103147 "description": "General-Purpose Collection Library for PHP",
103148 "keywords": [
103149 "collection",
103150 "list",
103151 "map",
103152 "sequence",
103153 "set"
103154 ],
103155 "time": "2013-02-25 13:15:41"
103156 },
103157 {
103158 "name": "phpoption/phpoption",
103159 "version": "dev-master",
103160 "source": {
103161 "type": "git",
103162 "url": "https://github.com/schmittjoh/php-option.git",
103163 "reference": "e686c0d55c87e042dec9c7fe3fa86b2b3b9d9ee3"
103164 },
103165 "dist": {
103166 "type": "zip",
103167 "url": "https://api.github.com/repos/schmittjoh/php-option/zipball/e686c0d55c87e042dec9c7fe3fa86b2b3b9d9ee3",
103168 "reference": "e686c0d55c87e042dec9c7fe3fa86b2b3b9d9ee3",
103169 "shasum": ""
103170 },
103171 "require": {
103172 "php": ">=5.3.0"
103173 },
103174 "type": "library",
103175 "extra": {
103176 "branch-alias": {
103177 "dev-master": "1.3-dev"
103178 }
103179 },
103180 "autoload": {
103181 "psr-0": {
103182 "PhpOption\\": "src/"
103183 }
103184 },
103185 "notification-url": "https://packagist.org/downloads/",
103186 "license": [
103187 "Apache2"
103188 ],
103189 "authors": [
103190 {
103191 "name": "Johannes Schmitt",
103192 "email": "schmittjoh@gmail.com",
103193 "homepage": "https://github.com/schmittjoh",
103194 "role": "Developer of wrapped JMSSerializerBundle"
103195 }
103196 ],
103197 "description": "Option Type for PHP",
103198 "keywords": [
103199 "language",
103200 "option",
103201 "php",
103202 "type"
103203 ],
103204 "time": "2013-03-28 16:34:57"
103205 }
103206 ],
103207 "packages-dev": [
103208 {
103209 "name": "doctrine/cache",
103210 "version": "dev-master",
103211 "source": {
103212 "type": "git",
103213 "url": "https://github.com/doctrine/cache.git",
103214 "reference": "4116eff37635219101a22c9732f409633eb07c5d"
103215 },
103216 "dist": {
103217 "type": "zip",
103218 "url": "https://api.github.com/repos/doctrine/cache/zipball/4116eff37635219101a22c9732f409633eb07c5d",
103219 "reference": "4116eff37635219101a22c9732f409633eb07c5d",
103220 "shasum": ""
103221 },
103222 "require": {
103223 "php": ">=5.3.2"
103224 },
103225 "type": "library",
103226 "extra": {
103227 "branch-alias": {
103228 "dev-master": "1.0.x-dev"
103229 }
103230 },
103231 "autoload": {
103232 "psr-0": {
103233 "Doctrine\\Common\\Cache\\": "lib/"
103234 }
103235 },
103236 "notification-url": "https://packagist.org/downloads/",
103237 "license": [
103238 "MIT"
103239 ],
103240 "authors": [
103241 {
103242 "name": "Jonathan Wage",
103243 "email": "jonwage@gmail.com",
103244 "homepage": "http://www.jwage.com/"
103245 },
103246 {
103247 "name": "Guilherme Blanco",
103248 "email": "guilhermeblanco@gmail.com",
103249 "homepage": "http://www.instaclick.com"
103250 },
103251 {
103252 "name": "Roman Borschel",
103253 "email": "roman@code-factory.org"
103254 },
103255 {
103256 "name": "Benjamin Eberlei",
103257 "email": "kontakt@beberlei.de"
103258 },
103259 {
103260 "name": "Johannes Schmitt",
103261 "email": "schmittjoh@gmail.com",
103262 "homepage": "https://github.com/schmittjoh",
103263 "role": "Developer of wrapped JMSSerializerBundle"
103264 }
103265 ],
103266 "description": "Caching library offering an object-oriented API for many cache backends",
103267 "homepage": "http://www.doctrine-project.org",
103268 "keywords": [
103269 "cache",
103270 "caching"
103271 ],
103272 "time": "2013-05-04 23:23:40"
103273 },
103274 {
103275 "name": "doctrine/collections",
103276 "version": "dev-master",
103277 "source": {
103278 "type": "git",
103279 "url": "https://github.com/doctrine/collections.git",
103280 "reference": "df2138bcb467533bfe6b3a01301d480aec008b93"
103281 },
103282 "dist": {
103283 "type": "zip",
103284 "url": "https://api.github.com/repos/doctrine/collections/zipball/df2138bcb467533bfe6b3a01301d480aec008b93",
103285 "reference": "df2138bcb467533bfe6b3a01301d480aec008b93",
103286 "shasum": ""
103287 },
103288 "require": {
103289 "php": ">=5.3.2"
103290 },
103291 "type": "library",
103292 "extra": {
103293 "branch-alias": {
103294 "dev-master": "1.1.x-dev"
103295 }
103296 },
103297 "autoload": {
103298 "psr-0": {
103299 "Doctrine\\Common\\Collections\\": "lib/"
103300 }
103301 },
103302 "notification-url": "https://packagist.org/downloads/",
103303 "license": [
103304 "MIT"
103305 ],
103306 "authors": [
103307 {
103308 "name": "Jonathan Wage",
103309 "email": "jonwage@gmail.com",
103310 "homepage": "http://www.jwage.com/"
103311 },
103312 {
103313 "name": "Guilherme Blanco",
103314 "email": "guilhermeblanco@gmail.com",
103315 "homepage": "http://www.instaclick.com"
103316 },
103317 {
103318 "name": "Roman Borschel",
103319 "email": "roman@code-factory.org"
103320 },
103321 {
103322 "name": "Benjamin Eberlei",
103323 "email": "kontakt@beberlei.de"
103324 },
103325 {
103326 "name": "Johannes Schmitt",
103327 "email": "schmittjoh@gmail.com",
103328 "homepage": "https://github.com/schmittjoh",
103329 "role": "Developer of wrapped JMSSerializerBundle"
103330 }
103331 ],
103332 "description": "Collections Abstraction library",
103333 "homepage": "http://www.doctrine-project.org",
103334 "keywords": [
103335 "array",
103336 "collections",
103337 "iterator"
103338 ],
103339 "time": "2013-03-17 14:01:33"
103340 },
103341 {
103342 "name": "doctrine/common",
103343 "version": "dev-master",
103344 "source": {
103345 "type": "git",
103346 "url": "https://github.com/doctrine/common.git",
103347 "reference": "ce87784a45f93c73a4035748fd98ecbbda3098fb"
103348 },
103349 "dist": {
103350 "type": "zip",
103351 "url": "https://api.github.com/repos/doctrine/common/zipball/ce87784a45f93c73a4035748fd98ecbbda3098fb",
103352 "reference": "ce87784a45f93c73a4035748fd98ecbbda3098fb",
103353 "shasum": ""
103354 },
103355 "require": {
103356 "doctrine/annotations": "1.*",
103357 "doctrine/cache": "1.*",
103358 "doctrine/collections": "1.*",
103359 "doctrine/inflector": "1.*",
103360 "doctrine/lexer": "1.*",
103361 "php": ">=5.3.2"
103362 },
103363 "type": "library",
103364 "extra": {
103365 "branch-alias": {
103366 "dev-master": "2.4.x-dev"
103367 }
103368 },
103369 "autoload": {
103370 "psr-0": {
103371 "Doctrine\\Common\\": "lib/"
103372 }
103373 },
103374 "notification-url": "https://packagist.org/downloads/",
103375 "license": [
103376 "MIT"
103377 ],
103378 "authors": [
103379 {
103380 "name": "Jonathan Wage",
103381 "email": "jonwage@gmail.com",
103382 "homepage": "http://www.jwage.com/"
103383 },
103384 {
103385 "name": "Guilherme Blanco",
103386 "email": "guilhermeblanco@gmail.com",
103387 "homepage": "http://www.instaclick.com"
103388 },
103389 {
103390 "name": "Roman Borschel",
103391 "email": "roman@code-factory.org"
103392 },
103393 {
103394 "name": "Benjamin Eberlei",
103395 "email": "kontakt@beberlei.de"
103396 },
103397 {
103398 "name": "Johannes Schmitt",
103399 "email": "schmittjoh@gmail.com",
103400 "homepage": "https://github.com/schmittjoh",
103401 "role": "Developer of wrapped JMSSerializerBundle"
103402 }
103403 ],
103404 "description": "Common Library for Doctrine projects",
103405 "homepage": "http://www.doctrine-project.org",
103406 "keywords": [
103407 "annotations",
103408 "collections",
103409 "eventmanager",
103410 "persistence",
103411 "spl"
103412 ],
103413 "time": "2013-05-09 21:35:24"
103414 },
103415 {
103416 "name": "doctrine/dbal",
103417 "version": "2.3.x-dev",
103418 "source": {
103419 "type": "git",
103420 "url": "https://github.com/doctrine/dbal.git",
103421 "reference": "587b5510a7c22f3a4829c933279a4ade8bf45c3a"
103422 },
103423 "dist": {
103424 "type": "zip",
103425 "url": "https://api.github.com/repos/doctrine/dbal/zipball/587b5510a7c22f3a4829c933279a4ade8bf45c3a",
103426 "reference": "587b5510a7c22f3a4829c933279a4ade8bf45c3a",
103427 "shasum": ""
103428 },
103429 "require": {
103430 "doctrine/common": ">=2.3.0,<2.5-dev",
103431 "php": ">=5.3.2"
103432 },
103433 "type": "library",
103434 "extra": {
103435 "branch-alias": {
103436 "dev-master": "2.3.x-dev"
103437 }
103438 },
103439 "autoload": {
103440 "psr-0": {
103441 "Doctrine\\DBAL": "lib/"
103442 }
103443 },
103444 "notification-url": "https://packagist.org/downloads/",
103445 "license": [
103446 "MIT"
103447 ],
103448 "authors": [
103449 {
103450 "name": "Jonathan Wage",
103451 "email": "jonwage@gmail.com",
103452 "homepage": "http://www.jwage.com/"
103453 },
103454 {
103455 "name": "Guilherme Blanco",
103456 "email": "guilhermeblanco@gmail.com",
103457 "homepage": "http://www.instaclick.com"
103458 },
103459 {
103460 "name": "Roman Borschel",
103461 "email": "roman@code-factory.org"
103462 },
103463 {
103464 "name": "Benjamin Eberlei",
103465 "email": "kontakt@beberlei.de"
103466 }
103467 ],
103468 "description": "Database Abstraction Layer",
103469 "homepage": "http://www.doctrine-project.org",
103470 "keywords": [
103471 "database",
103472 "dbal",
103473 "persistence",
103474 "queryobject"
103475 ],
103476 "time": "2013-05-09 14:04:51"
103477 },
103478 {
103479 "name": "doctrine/inflector",
103480 "version": "dev-master",
103481 "source": {
103482 "type": "git",
103483 "url": "https://github.com/doctrine/inflector.git",
103484 "reference": "8b4b3ccec7aafc596e2fc1e593c9f2e78f939c8c"
103485 },
103486 "dist": {
103487 "type": "zip",
103488 "url": "https://api.github.com/repos/doctrine/inflector/zipball/8b4b3ccec7aafc596e2fc1e593c9f2e78f939c8c",
103489 "reference": "8b4b3ccec7aafc596e2fc1e593c9f2e78f939c8c",
103490 "shasum": ""
103491 },
103492 "require": {
103493 "php": ">=5.3.2"
103494 },
103495 "type": "library",
103496 "extra": {
103497 "branch-alias": {
103498 "dev-master": "1.0.x-dev"
103499 }
103500 },
103501 "autoload": {
103502 "psr-0": {
103503 "Doctrine\\Common\\Inflector\\": "lib/"
103504 }
103505 },
103506 "notification-url": "https://packagist.org/downloads/",
103507 "license": [
103508 "MIT"
103509 ],
103510 "authors": [
103511 {
103512 "name": "Jonathan Wage",
103513 "email": "jonwage@gmail.com",
103514 "homepage": "http://www.jwage.com/"
103515 },
103516 {
103517 "name": "Guilherme Blanco",
103518 "email": "guilhermeblanco@gmail.com",
103519 "homepage": "http://www.instaclick.com"
103520 },
103521 {
103522 "name": "Roman Borschel",
103523 "email": "roman@code-factory.org"
103524 },
103525 {
103526 "name": "Benjamin Eberlei",
103527 "email": "kontakt@beberlei.de"
103528 },
103529 {
103530 "name": "Johannes Schmitt",
103531 "email": "schmittjoh@gmail.com",
103532 "homepage": "https://github.com/schmittjoh",
103533 "role": "Developer of wrapped JMSSerializerBundle"
103534 }
103535 ],
103536 "description": "Common String Manipulations with regard to casing and singular/plural rules.",
103537 "homepage": "http://www.doctrine-project.org",
103538 "keywords": [
103539 "inflection",
103540 "pluralize",
103541 "singularize",
103542 "string"
103543 ],
103544 "time": "2013-04-10 16:14:30"
103545 },
103546 {
103547 "name": "doctrine/orm",
103548 "version": "2.3.x-dev",
103549 "source": {
103550 "type": "git",
103551 "url": "https://github.com/doctrine/doctrine2.git",
103552 "reference": "e0feccc2e46021689d3c5cf2a1d1e920a060b8ad"
103553 },
103554 "dist": {
103555 "type": "zip",
103556 "url": "https://api.github.com/repos/doctrine/doctrine2/zipball/e0feccc2e46021689d3c5cf2a1d1e920a060b8ad",
103557 "reference": "e0feccc2e46021689d3c5cf2a1d1e920a060b8ad",
103558 "shasum": ""
103559 },
103560 "require": {
103561 "doctrine/dbal": "2.3.*",
103562 "ext-pdo": "*",
103563 "php": ">=5.3.2",
103564 "symfony/console": "2.*"
103565 },
103566 "suggest": {
103567 "symfony/yaml": "If you want to use YAML Metadata Mapping Driver"
103568 },
103569 "bin": [
103570 "bin/doctrine",
103571 "bin/doctrine.php"
103572 ],
103573 "type": "library",
103574 "extra": {
103575 "branch-alias": {
103576 "dev-master": "2.3.x-dev"
103577 }
103578 },
103579 "autoload": {
103580 "psr-0": {
103581 "Doctrine\\ORM": "lib/"
103582 }
103583 },
103584 "notification-url": "https://packagist.org/downloads/",
103585 "license": [
103586 "MIT"
103587 ],
103588 "authors": [
103589 {
103590 "name": "Jonathan Wage",
103591 "email": "jonwage@gmail.com",
103592 "homepage": "http://www.jwage.com/"
103593 },
103594 {
103595 "name": "Guilherme Blanco",
103596 "email": "guilhermeblanco@gmail.com",
103597 "homepage": "http://www.instaclick.com"
103598 },
103599 {
103600 "name": "Roman Borschel",
103601 "email": "roman@code-factory.org"
103602 },
103603 {
103604 "name": "Benjamin Eberlei",
103605 "email": "kontakt@beberlei.de"
103606 }
103607 ],
103608 "description": "Object-Relational-Mapper for PHP",
103609 "homepage": "http://www.doctrine-project.org",
103610 "keywords": [
103611 "database",
103612 "orm"
103613 ],
103614 "time": "2013-05-09 16:17:03"
103615 },
103616 {
103617 "name": "symfony/console",
103618 "version": "dev-master",
103619 "target-dir": "Symfony/Component/Console",
103620 "source": {
103621 "type": "git",
103622 "url": "https://github.com/symfony/Console.git",
103623 "reference": "v2.3.0-BETA2"
103624 },
103625 "dist": {
103626 "type": "zip",
103627 "url": "https://api.github.com/repos/symfony/Console/zipball/v2.3.0-BETA2",
103628 "reference": "v2.3.0-BETA2",
103629 "shasum": ""
103630 },
103631 "require": {
103632 "php": ">=5.3.3"
103633 },
103634 "require-dev": {
103635 "symfony/event-dispatcher": ">=2.1,<3.0"
103636 },
103637 "type": "library",
103638 "extra": {
103639 "branch-alias": {
103640 "dev-master": "2.3-dev"
103641 }
103642 },
103643 "autoload": {
103644 "psr-0": {
103645 "Symfony\\Component\\Console\\": ""
103646 }
103647 },
103648 "notification-url": "https://packagist.org/downloads/",
103649 "license": [
103650 "MIT"
103651 ],
103652 "authors": [
103653 {
103654 "name": "Fabien Potencier",
103655 "email": "fabien@symfony.com"
103656 },
103657 {
103658 "name": "Symfony Community",
103659 "homepage": "http://symfony.com/contributors"
103660 }
103661 ],
103662 "description": "Symfony Console Component",
103663 "homepage": "http://symfony.com",
103664 "time": "2013-05-10 18:12:13"
103665 },
103666 {
103667 "name": "symfony/event-dispatcher",
103668 "version": "2.1.x-dev",
103669 "target-dir": "Symfony/Component/EventDispatcher",
103670 "source": {
103671 "type": "git",
103672 "url": "https://github.com/symfony/EventDispatcher.git",
103673 "reference": "v2.1.10"
103674 },
103675 "dist": {
103676 "type": "zip",
103677 "url": "https://api.github.com/repos/symfony/EventDispatcher/zipball/v2.1.10",
103678 "reference": "v2.1.10",
103679 "shasum": ""
103680 },
103681 "require": {
103682 "php": ">=5.3.3"
103683 },
103684 "require-dev": {
103685 "symfony/dependency-injection": "2.1.*"
103686 },
103687 "suggest": {
103688 "symfony/dependency-injection": "2.1.*",
103689 "symfony/http-kernel": "2.1.*"
103690 },
103691 "type": "library",
103692 "autoload": {
103693 "psr-0": {
103694 "Symfony\\Component\\EventDispatcher": ""
103695 }
103696 },
103697 "notification-url": "https://packagist.org/downloads/",
103698 "license": [
103699 "MIT"
103700 ],
103701 "authors": [
103702 {
103703 "name": "Fabien Potencier",
103704 "email": "fabien@symfony.com"
103705 },
103706 {
103707 "name": "Symfony Community",
103708 "homepage": "http://symfony.com/contributors"
103709 }
103710 ],
103711 "description": "Symfony EventDispatcher Component",
103712 "homepage": "http://symfony.com",
103713 "time": "2013-02-11 11:26:14"
103714 },
103715 {
103716 "name": "symfony/filesystem",
103717 "version": "dev-master",
103718 "target-dir": "Symfony/Component/Filesystem",
103719 "source": {
103720 "type": "git",
103721 "url": "https://github.com/symfony/Filesystem.git",
103722 "reference": "v2.3.0-BETA2"
103723 },
103724 "dist": {
103725 "type": "zip",
103726 "url": "https://api.github.com/repos/symfony/Filesystem/zipball/v2.3.0-BETA2",
103727 "reference": "v2.3.0-BETA2",
103728 "shasum": ""
103729 },
103730 "require": {
103731 "php": ">=5.3.3"
103732 },
103733 "type": "library",
103734 "extra": {
103735 "branch-alias": {
103736 "dev-master": "2.3-dev"
103737 }
103738 },
103739 "autoload": {
103740 "psr-0": {
103741 "Symfony\\Component\\Filesystem\\": ""
103742 }
103743 },
103744 "notification-url": "https://packagist.org/downloads/",
103745 "license": [
103746 "MIT"
103747 ],
103748 "authors": [
103749 {
103750 "name": "Fabien Potencier",
103751 "email": "fabien@symfony.com"
103752 },
103753 {
103754 "name": "Symfony Community",
103755 "homepage": "http://symfony.com/contributors"
103756 }
103757 ],
103758 "description": "Symfony Filesystem Component",
103759 "homepage": "http://symfony.com",
103760 "time": "2013-05-06 20:03:44"
103761 },
103762 {
103763 "name": "symfony/form",
103764 "version": "2.1.x-dev",
103765 "target-dir": "Symfony/Component/Form",
103766 "source": {
103767 "type": "git",
103768 "url": "https://github.com/symfony/Form.git",
103769 "reference": "v2.1.10"
103770 },
103771 "dist": {
103772 "type": "zip",
103773 "url": "https://api.github.com/repos/symfony/Form/zipball/v2.1.10",
103774 "reference": "v2.1.10",
103775 "shasum": ""
103776 },
103777 "require": {
103778 "php": ">=5.3.3",
103779 "symfony/event-dispatcher": "2.1.*",
103780 "symfony/locale": "2.1.*",
103781 "symfony/options-resolver": "2.1.*"
103782 },
103783 "require-dev": {
103784 "symfony/http-foundation": "2.1.*",
103785 "symfony/validator": "2.1.*"
103786 },
103787 "suggest": {
103788 "symfony/http-foundation": "2.1.*",
103789 "symfony/validator": "2.1.*"
103790 },
103791 "type": "library",
103792 "autoload": {
103793 "psr-0": {
103794 "Symfony\\Component\\Form": ""
103795 }
103796 },
103797 "notification-url": "https://packagist.org/downloads/",
103798 "license": [
103799 "MIT"
103800 ],
103801 "authors": [
103802 {
103803 "name": "Fabien Potencier",
103804 "email": "fabien@symfony.com"
103805 },
103806 {
103807 "name": "Symfony Community",
103808 "homepage": "http://symfony.com/contributors"
103809 }
103810 ],
103811 "description": "Symfony Form Component",
103812 "homepage": "http://symfony.com",
103813 "time": "2013-05-06 10:48:41"
103814 },
103815 {
103816 "name": "symfony/locale",
103817 "version": "2.1.x-dev",
103818 "target-dir": "Symfony/Component/Locale",
103819 "source": {
103820 "type": "git",
103821 "url": "https://github.com/symfony/Locale.git",
103822 "reference": "v2.1.10"
103823 },
103824 "dist": {
103825 "type": "zip",
103826 "url": "https://api.github.com/repos/symfony/Locale/zipball/v2.1.10",
103827 "reference": "v2.1.10",
103828 "shasum": ""
103829 },
103830 "require": {
103831 "php": ">=5.3.3"
103832 },
103833 "suggest": {
103834 "ext-intl": ">=5.3.3"
103835 },
103836 "type": "library",
103837 "autoload": {
103838 "psr-0": {
103839 "Symfony\\Component\\Locale": ""
103840 }
103841 },
103842 "notification-url": "https://packagist.org/downloads/",
103843 "license": [
103844 "MIT"
103845 ],
103846 "authors": [
103847 {
103848 "name": "Fabien Potencier",
103849 "email": "fabien@symfony.com"
103850 },
103851 {
103852 "name": "Symfony Community",
103853 "homepage": "http://symfony.com/contributors"
103854 }
103855 ],
103856 "description": "Symfony Locale Component",
103857 "homepage": "http://symfony.com",
103858 "time": "2013-01-10 04:41:59"
103859 },
103860 {
103861 "name": "symfony/options-resolver",
103862 "version": "2.1.x-dev",
103863 "target-dir": "Symfony/Component/OptionsResolver",
103864 "source": {
103865 "type": "git",
103866 "url": "https://github.com/symfony/OptionsResolver.git",
103867 "reference": "v2.1.10"
103868 },
103869 "dist": {
103870 "type": "zip",
103871 "url": "https://api.github.com/repos/symfony/OptionsResolver/zipball/v2.1.10",
103872 "reference": "v2.1.10",
103873 "shasum": ""
103874 },
103875 "require": {
103876 "php": ">=5.3.3"
103877 },
103878 "type": "library",
103879 "autoload": {
103880 "psr-0": {
103881 "Symfony\\Component\\OptionsResolver": ""
103882 }
103883 },
103884 "notification-url": "https://packagist.org/downloads/",
103885 "license": [
103886 "MIT"
103887 ],
103888 "authors": [
103889 {
103890 "name": "Fabien Potencier",
103891 "email": "fabien@symfony.com"
103892 },
103893 {
103894 "name": "Symfony Community",
103895 "homepage": "http://symfony.com/contributors"
103896 }
103897 ],
103898 "description": "Symfony OptionsResolver Component",
103899 "homepage": "http://symfony.com",
103900 "keywords": [
103901 "config",
103902 "configuration",
103903 "options"
103904 ],
103905 "time": "2013-01-09 08:51:07"
103906 },
103907 {
103908 "name": "symfony/translation",
103909 "version": "2.1.x-dev",
103910 "target-dir": "Symfony/Component/Translation",
103911 "source": {
103912 "type": "git",
103913 "url": "https://github.com/symfony/Translation.git",
103914 "reference": "v2.1.10"
103915 },
103916 "dist": {
103917 "type": "zip",
103918 "url": "https://api.github.com/repos/symfony/Translation/zipball/v2.1.10",
103919 "reference": "v2.1.10",
103920 "shasum": ""
103921 },
103922 "require": {
103923 "php": ">=5.3.3"
103924 },
103925 "require-dev": {
103926 "symfony/config": "2.1.*",
103927 "symfony/yaml": "2.1.*"
103928 },
103929 "suggest": {
103930 "symfony/config": "2.1.*",
103931 "symfony/yaml": "2.1.*"
103932 },
103933 "type": "library",
103934 "autoload": {
103935 "psr-0": {
103936 "Symfony\\Component\\Translation": ""
103937 }
103938 },
103939 "notification-url": "https://packagist.org/downloads/",
103940 "license": [
103941 "MIT"
103942 ],
103943 "authors": [
103944 {
103945 "name": "Fabien Potencier",
103946 "email": "fabien@symfony.com"
103947 },
103948 {
103949 "name": "Symfony Community",
103950 "homepage": "http://symfony.com/contributors"
103951 }
103952 ],
103953 "description": "Symfony Translation Component",
103954 "homepage": "http://symfony.com",
103955 "time": "2013-04-20 08:25:59"
103956 },
103957 {
103958 "name": "symfony/validator",
103959 "version": "2.1.x-dev",
103960 "target-dir": "Symfony/Component/Validator",
103961 "source": {
103962 "type": "git",
103963 "url": "https://github.com/symfony/Validator.git",
103964 "reference": "bd1aa1eb092f3f01cd87e9c18e959cbb07cf7579"
103965 },
103966 "dist": {
103967 "type": "zip",
103968 "url": "https://api.github.com/repos/symfony/Validator/zipball/bd1aa1eb092f3f01cd87e9c18e959cbb07cf7579",
103969 "reference": "bd1aa1eb092f3f01cd87e9c18e959cbb07cf7579",
103970 "shasum": ""
103971 },
103972 "require": {
103973 "php": ">=5.3.3"
103974 },
103975 "require-dev": {
103976 "symfony/http-foundation": "2.1.*",
103977 "symfony/locale": "2.1.*",
103978 "symfony/yaml": "2.1.*"
103979 },
103980 "suggest": {
103981 "doctrine/common": "~2.1",
103982 "symfony/http-foundation": "2.1.*",
103983 "symfony/yaml": "2.1.*"
103984 },
103985 "type": "library",
103986 "autoload": {
103987 "psr-0": {
103988 "Symfony\\Component\\Validator": ""
103989 }
103990 },
103991 "notification-url": "https://packagist.org/downloads/",
103992 "license": [
103993 "MIT"
103994 ],
103995 "authors": [
103996 {
103997 "name": "Fabien Potencier",
103998 "email": "fabien@symfony.com"
103999 },
104000 {
104001 "name": "Symfony Community",
104002 "homepage": "http://symfony.com/contributors"
104003 }
104004 ],
104005 "description": "Symfony Validator Component",
104006 "homepage": "http://symfony.com",
104007 "time": "2013-05-10 16:13:09"
104008 },
104009 {
104010 "name": "symfony/yaml",
104011 "version": "dev-master",
104012 "target-dir": "Symfony/Component/Yaml",
104013 "source": {
104014 "type": "git",
104015 "url": "https://github.com/symfony/Yaml.git",
104016 "reference": "030e5f857f4089912ea3ee01d84ef41bc9a961a4"
104017 },
104018 "dist": {
104019 "type": "zip",
104020 "url": "https://api.github.com/repos/symfony/Yaml/zipball/030e5f857f4089912ea3ee01d84ef41bc9a961a4",
104021 "reference": "030e5f857f4089912ea3ee01d84ef41bc9a961a4",
104022 "shasum": ""
104023 },
104024 "require": {
104025 "php": ">=5.3.3"
104026 },
104027 "type": "library",
104028 "extra": {
104029 "branch-alias": {
104030 "dev-master": "2.3-dev"
104031 }
104032 },
104033 "autoload": {
104034 "psr-0": {
104035 "Symfony\\Component\\Yaml\\": ""
104036 }
104037 },
104038 "notification-url": "https://packagist.org/downloads/",
104039 "license": [
104040 "MIT"
104041 ],
104042 "authors": [
104043 {
104044 "name": "Fabien Potencier",
104045 "email": "fabien@symfony.com"
104046 },
104047 {
104048 "name": "Symfony Community",
104049 "homepage": "http://symfony.com/contributors"
104050 }
104051 ],
104052 "description": "Symfony Yaml Component",
104053 "homepage": "http://symfony.com",
104054 "time": "2013-05-10 18:12:13"
104055 },
104056 {
104057 "name": "twig/twig",
104058 "version": "dev-master",
104059 "source": {
104060 "type": "git",
104061 "url": "https://github.com/fabpot/Twig.git",
104062 "reference": "4a49a3ae30ad2694057a63eccc3fb7b551b93e74"
104063 },
104064 "dist": {
104065 "type": "zip",
104066 "url": "https://api.github.com/repos/fabpot/Twig/zipball/4a49a3ae30ad2694057a63eccc3fb7b551b93e74",
104067 "reference": "4a49a3ae30ad2694057a63eccc3fb7b551b93e74",
104068 "shasum": ""
104069 },
104070 "require": {
104071 "php": ">=5.2.4"
104072 },
104073 "type": "library",
104074 "extra": {
104075 "branch-alias": {
104076 "dev-master": "1.13-dev"
104077 }
104078 },
104079 "autoload": {
104080 "psr-0": {
104081 "Twig_": "lib/"
104082 }
104083 },
104084 "notification-url": "https://packagist.org/downloads/",
104085 "license": [
104086 "BSD-3"
104087 ],
104088 "authors": [
104089 {
104090 "name": "Fabien Potencier",
104091 "email": "fabien@symfony.com"
104092 },
104093 {
104094 "name": "Armin Ronacher",
104095 "email": "armin.ronacher@active-4.com"
104096 }
104097 ],
104098 "description": "Twig, the flexible, fast, and secure template language for PHP",
104099 "homepage": "http://twig.sensiolabs.org",
104100 "keywords": [
104101 "templating"
104102 ],
104103 "time": "2013-05-10 15:25:39"
104104 }
104105 ],
104106 "aliases": [
104107
104108 ],
104109 "minimum-stability": "dev",
104110 "stability-flags": [
104111
104112 ],
104113 "platform": {
104114 "php": ">=5.3.2"
104115 },
104116 "platform-dev": [
104117
104118 ]
104119 }
104120 THE WORK (AS DEFINED BELOW) IS PROVIDED UNDER THE TERMS OF THIS CREATIVE COMMONS PUBLIC LICENSE ("CCPL" OR "LICENSE"). THE WORK IS PROTECTED BY COPYRIGHT AND/OR OTHER APPLICABLE LAW. ANY USE OF THE WORK OTHER THAN AS AUTHORIZED UNDER THIS LICENSE OR COPYRIGHT LAW IS PROHIBITED.
104121
104122 BY EXERCISING ANY RIGHTS TO THE WORK PROVIDED HERE, YOU ACCEPT AND AGREE TO BE BOUND BY THE TERMS OF THIS LICENSE. TO THE EXTENT THIS LICENSE MAY BE CONSIDERED TO BE A CONTRACT, THE LICENSOR GRANTS YOU THE RIGHTS CONTAINED HERE IN CONSIDERATION OF YOUR ACCEPTANCE OF SUCH TERMS AND CONDITIONS.
104123
104124 1. Definitions
104125
104126 "Adaptation" means a work based upon the Work, or upon the Work and other pre-existing works, such as a translation, adaptation, derivative work, arrangement of music or other alterations of a literary or artistic work, or phonogram or performance and includes cinematographic adaptations or any other form in which the Work may be recast, transformed, or adapted including in any form recognizably derived from the original, except that a work that constitutes a Collection will not be considered an Adaptation for the purpose of this License. For the avoidance of doubt, where the Work is a musical work, performance or phonogram, the synchronization of the Work in timed-relation with a moving image ("synching") will be considered an Adaptation for the purpose of this License.
104127 "Collection" means a collection of literary or artistic works, such as encyclopedias and anthologies, or performances, phonograms or broadcasts, or other works or subject matter other than works listed in Section 1(f) below, which, by reason of the selection and arrangement of their contents, constitute intellectual creations, in which the Work is included in its entirety in unmodified form along with one or more other contributions, each constituting separate and independent works in themselves, which together are assembled into a collective whole. A work that constitutes a Collection will not be considered an Adaptation (as defined above) for the purposes of this License.
104128 "Distribute" means to make available to the public the original and copies of the Work through sale or other transfer of ownership.
104129 "Licensor" means the individual, individuals, entity or entities that offer(s) the Work under the terms of this License.
104130 "Original Author" means, in the case of a literary or artistic work, the individual, individuals, entity or entities who created the Work or if no individual or entity can be identified, the publisher; and in addition (i) in the case of a performance the actors, singers, musicians, dancers, and other persons who act, sing, deliver, declaim, play in, interpret or otherwise perform literary or artistic works or expressions of folklore; (ii) in the case of a phonogram the producer being the person or legal entity who first fixes the sounds of a performance or other sounds; and, (iii) in the case of broadcasts, the organization that transmits the broadcast.
104131 "Work" means the literary and/or artistic work offered under the terms of this License including without limitation any production in the literary, scientific and artistic domain, whatever may be the mode or form of its expression including digital form, such as a book, pamphlet and other writing; a lecture, address, sermon or other work of the same nature; a dramatic or dramatico-musical work; a choreographic work or entertainment in dumb show; a musical composition with or without words; a cinematographic work to which are assimilated works expressed by a process analogous to cinematography; a work of drawing, painting, architecture, sculpture, engraving or lithography; a photographic work to which are assimilated works expressed by a process analogous to photography; a work of applied art; an illustration, map, plan, sketch or three-dimensional work relative to geography, topography, architecture or science; a performance; a broadcast; a phonogram; a compilation of data to the extent it is protected as a copyrightable work; or a work performed by a variety or circus performer to the extent it is not otherwise considered a literary or artistic work.
104132 "You" means an individual or entity exercising rights under this License who has not previously violated the terms of this License with respect to the Work, or who has received express permission from the Licensor to exercise rights under this License despite a previous violation.
104133 "Publicly Perform" means to perform public recitations of the Work and to communicate to the public those public recitations, by any means or process, including by wire or wireless means or public digital performances; to make available to the public Works in such a way that members of the public may access these Works from a place and at a place individually chosen by them; to perform the Work to the public by any means or process and the communication to the public of the performances of the Work, including by public digital performance; to broadcast and rebroadcast the Work by any means including signs, sounds or images.
104134 "Reproduce" means to make copies of the Work by any means including without limitation by sound or visual recordings and the right of fixation and reproducing fixations of the Work, including storage of a protected performance or phonogram in digital form or other electronic medium.
104135
104136 2. Fair Dealing Rights. Nothing in this License is intended to reduce, limit, or restrict any uses free from copyright or rights arising from limitations or exceptions that are provided for in connection with the copyright protection under copyright law or other applicable laws.
104137
104138 3. License Grant. Subject to the terms and conditions of this License, Licensor hereby grants You a worldwide, royalty-free, non-exclusive, perpetual (for the duration of the applicable copyright) license to exercise the rights in the Work as stated below:
104139
104140 to Reproduce the Work, to incorporate the Work into one or more Collections, and to Reproduce the Work as incorporated in the Collections; and,
104141 to Distribute and Publicly Perform the Work including as incorporated in Collections.
104142
104143 The above rights may be exercised in all media and formats whether now known or hereafter devised. The above rights include the right to make such modifications as are technically necessary to exercise the rights in other media and formats, but otherwise you have no rights to make Adaptations. Subject to 8(f), all rights not expressly granted by Licensor are hereby reserved, including but not limited to the rights set forth in Section 4(d).
104144
104145 4. Restrictions. The license granted in Section 3 above is expressly made subject to and limited by the following restrictions:
104146
104147 You may Distribute or Publicly Perform the Work only under the terms of this License. You must include a copy of, or the Uniform Resource Identifier (URI) for, this License with every copy of the Work You Distribute or Publicly Perform. You may not offer or impose any terms on the Work that restrict the terms of this License or the ability of the recipient of the Work to exercise the rights granted to that recipient under the terms of the License. You may not sublicense the Work. You must keep intact all notices that refer to this License and to the disclaimer of warranties with every copy of the Work You Distribute or Publicly Perform. When You Distribute or Publicly Perform the Work, You may not impose any effective technological measures on the Work that restrict the ability of a recipient of the Work from You to exercise the rights granted to that recipient under the terms of the License. This Section 4(a) applies to the Work as incorporated in a Collection, but this does not require the Collection apart from the Work itself to be made subject to the terms of this License. If You create a Collection, upon notice from any Licensor You must, to the extent practicable, remove from the Collection any credit as required by Section 4(c), as requested.
104148 You may not exercise any of the rights granted to You in Section 3 above in any manner that is primarily intended for or directed toward commercial advantage or private monetary compensation. The exchange of the Work for other copyrighted works by means of digital file-sharing or otherwise shall not be considered to be intended for or directed toward commercial advantage or private monetary compensation, provided there is no payment of any monetary compensation in connection with the exchange of copyrighted works.
104149 If You Distribute, or Publicly Perform the Work or Collections, You must, unless a request has been made pursuant to Section 4(a), keep intact all copyright notices for the Work and provide, reasonable to the medium or means You are utilizing: (i) the name of the Original Author (or pseudonym, if applicable) if supplied, and/or if the Original Author and/or Licensor designate another party or parties (e.g., a sponsor institute, publishing entity, journal) for attribution ("Attribution Parties") in Licensor's copyright notice, terms of service or by other reasonable means, the name of such party or parties; (ii) the title of the Work if supplied; (iii) to the extent reasonably practicable, the URI, if any, that Licensor specifies to be associated with the Work, unless such URI does not refer to the copyright notice or licensing information for the Work. The credit required by this Section 4(c) may be implemented in any reasonable manner; provided, however, that in the case of a Collection, at a minimum such credit will appear, if a credit for all contributing authors of Collection appears, then as part of these credits and in a manner at least as prominent as the credits for the other contributing authors. For the avoidance of doubt, You may only use the credit required by this Section for the purpose of attribution in the manner set out above and, by exercising Your rights under this License, You may not implicitly or explicitly assert or imply any connection with, sponsorship or endorsement by the Original Author, Licensor and/or Attribution Parties, as appropriate, of You or Your use of the Work, without the separate, express prior written permission of the Original Author, Licensor and/or Attribution Parties.
104150
104151 For the avoidance of doubt:
104152 Non-waivable Compulsory License Schemes. In those jurisdictions in which the right to collect royalties through any statutory or compulsory licensing scheme cannot be waived, the Licensor reserves the exclusive right to collect such royalties for any exercise by You of the rights granted under this License;
104153 Waivable Compulsory License Schemes. In those jurisdictions in which the right to collect royalties through any statutory or compulsory licensing scheme can be waived, the Licensor reserves the exclusive right to collect such royalties for any exercise by You of the rights granted under this License if Your exercise of such rights is for a purpose or use which is otherwise than noncommercial as permitted under Section 4(b) and otherwise waives the right to collect royalties through any statutory or compulsory licensing scheme; and,
104154 Voluntary License Schemes. The Licensor reserves the right to collect royalties, whether individually or, in the event that the Licensor is a member of a collecting society that administers voluntary licensing schemes, via that society, from any exercise by You of the rights granted under this License that is for a purpose or use which is otherwise than noncommercial as permitted under Section 4(b).
104155 Except as otherwise agreed in writing by the Licensor or as may be otherwise permitted by applicable law, if You Reproduce, Distribute or Publicly Perform the Work either by itself or as part of any Collections, You must not distort, mutilate, modify or take other derogatory action in relation to the Work which would be prejudicial to the Original Author's honor or reputation.
104156
104157 5. Representations, Warranties and Disclaimer
104158
104159 UNLESS OTHERWISE MUTUALLY AGREED BY THE PARTIES IN WRITING, LICENSOR OFFERS THE WORK AS-IS AND MAKES NO REPRESENTATIONS OR WARRANTIES OF ANY KIND CONCERNING THE WORK, EXPRESS, IMPLIED, STATUTORY OR OTHERWISE, INCLUDING, WITHOUT LIMITATION, WARRANTIES OF TITLE, MERCHANTIBILITY, FITNESS FOR A PARTICULAR PURPOSE, NONINFRINGEMENT, OR THE ABSENCE OF LATENT OR OTHER DEFECTS, ACCURACY, OR THE PRESENCE OF ABSENCE OF ERRORS, WHETHER OR NOT DISCOVERABLE. SOME JURISDICTIONS DO NOT ALLOW THE EXCLUSION OF IMPLIED WARRANTIES, SO SUCH EXCLUSION MAY NOT APPLY TO YOU.
104160
104161 6. Limitation on Liability. EXCEPT TO THE EXTENT REQUIRED BY APPLICABLE LAW, IN NO EVENT WILL LICENSOR BE LIABLE TO YOU ON ANY LEGAL THEORY FOR ANY SPECIAL, INCIDENTAL, CONSEQUENTIAL, PUNITIVE OR EXEMPLARY DAMAGES ARISING OUT OF THIS LICENSE OR THE USE OF THE WORK, EVEN IF LICENSOR HAS BEEN ADVISED OF THE POSSIBILITY OF SUCH DAMAGES.
104162
104163 7. Termination
104164
104165 This License and the rights granted hereunder will terminate automatically upon any breach by You of the terms of this License. Individuals or entities who have received Collections from You under this License, however, will not have their licenses terminated provided such individuals or entities remain in full compliance with those licenses. Sections 1, 2, 5, 6, 7, and 8 will survive any termination of this License.
104166 Subject to the above terms and conditions, the license granted here is perpetual (for the duration of the applicable copyright in the Work). Notwithstanding the above, Licensor reserves the right to release the Work under different license terms or to stop distributing the Work at any time; provided, however that any such election will not serve to withdraw this License (or any other license that has been, or is required to be, granted under the terms of this License), and this License will continue in full force and effect unless terminated as stated above.
104167
104168 8. Miscellaneous
104169
104170 Each time You Distribute or Publicly Perform the Work or a Collection, the Licensor offers to the recipient a license to the Work on the same terms and conditions as the license granted to You under this License.
104171 If any provision of this License is invalid or unenforceable under applicable law, it shall not affect the validity or enforceability of the remainder of the terms of this License, and without further action by the parties to this agreement, such provision shall be reformed to the minimum extent necessary to make such provision valid and enforceable.
104172 No term or provision of this License shall be deemed waived and no breach consented to unless such waiver or consent shall be in writing and signed by the party to be charged with such waiver or consent.
104173 This License constitutes the entire agreement between the parties with respect to the Work licensed here. There are no understandings, agreements or representations with respect to the Work not specified here. Licensor shall not be bound by any additional provisions that may appear in any communication from You. This License may not be modified without the mutual written agreement of the Licensor and You.
104174 The rights granted under, and the subject matter referenced, in this License were drafted utilizing the terminology of the Berne Convention for the Protection of Literary and Artistic Works (as amended on September 28, 1979), the Rome Convention of 1961, the WIPO Copyright Treaty of 1996, the WIPO Performances and Phonograms Treaty of 1996 and the Universal Copyright Convention (as revised on July 24, 1971). These rights and subject matter take effect in the relevant jurisdiction in which the License terms are sought to be enforced according to the corresponding provisions of the implementation of those treaty provisions in the applicable national law. If the standard suite of rights granted under applicable copyright law includes additional rights not granted under this License, such additional rights are deemed to be included in the License; this License is not intended to restrict the license of any rights under applicable law.
104175 Event System
104176 ============
104177
104178 The serializer dispatches different events during the serialization, and
104179 deserialization process which you can use to hook in and alter the default
104180 behavior.
104181
104182 Register an Event Listener, or Subscriber
104183 -----------------------------------------
104184 The difference between listeners, and subscribers is that listener do not know to which events they listen
104185 while subscribers contain that information. Thus, subscribers are easier to share, and re-use. Listeners
104186 on the other hand, can be simple callables and do not require a dedicated class.
104187
104188 .. code-block :: php
104189
104190 class MyEventSubscriber implements JMS\Serializer\EventDispatcher\EventSubscriberInterface
104191 {
104192 public static function getSubscribedEvents()
104193 {
104194 return array(
104195 array('event' => 'serializer.pre_serialize', 'method' => 'onPreSerialize'),
104196 );
104197 }
104198
104199 public function onPreSerialize(JMS\Serializer\EventDispatcher\PreSerializeEvent $event)
104200 {
104201 // do something
104202 }
104203 }
104204
104205 $builder
104206 ->configureListeners(function(JMS\Serializer\EventDispatcher\EventDispatcher $dispatcher) {
104207 $dispatcher->addListener('serializer.pre_serialize',
104208 function(JMS\Serializer\EventDispatcher\PreSerializeEvent $event) {
104209 // do something
104210 }
104211 );
104212
104213 $dispatcher->addSubscriber(new MyEventSubscriber());
104214 })
104215 ;
104216
104217 Events
104218 ------
104219
104220 serializer.pre_serialize
104221 ~~~~~~~~~~~~~~~~~~~~~~~~
104222 This is dispatched before a type is visited. You have access to the visitor,
104223 data, and type. Listeners may modify the type that is being used for
104224 serialization.
104225
104226 **Event Object**: ``JMS\Serializer\EventDispatcher\PreSerializeEvent``
104227
104228 serializer.post_serialize
104229 ~~~~~~~~~~~~~~~~~~~~~~~~~
104230 This is dispatched right before a type is left. You can for example use this
104231 to add additional data for an object that you normally do not save inside
104232 objects such as links.
104233
104234 **Event Object**: ``JMS\Serializer\EventDispatcher\ObjectEvent``
104235
104236 serializer.pre_deserialize
104237 ~~~~~~~~~~~~~~~~~~~~~~~~~~~
104238
104239 .. versionadded : 0.12
104240 Event was added
104241
104242 This is dispatched before an object is deserialized. You can use this to
104243 modify submitted data, or modify the type that is being used for deserialization.
104244
104245 **Event Object**: ``JMS\Serializer\EventDispatcher\PreDeserializeEvent``
104246
104247 serializer.post_deserialize
104248 ~~~~~~~~~~~~~~~~~~~~~~~~~~~
104249 This is dispatched after a type is processed. You can use it to normalize
104250 submitted data if you require external services for example, or also to
104251 perform validation of the submitted data.
104252
104253 **Event Object**: ``JMS\Serializer\EventDispatcher\ObjectEvent``
104254 Serializer
104255 ==========
104256
104257 Introduction
104258 ------------
104259 This library allows you to (de-)serialize data of any complexity. Currently, it supports XML, JSON, and YAML.
104260
104261 It also provides you with a rich tool-set to adapt the output to your specific needs.
104262
104263 Built-in features include:
104264
104265 - (De-)serialize data of any complexity; circular references are handled gracefully.
104266 - Supports many built-in PHP types (such as dates)
104267 - Integrates with Doctrine ORM, et. al.
104268 - Supports versioning, e.g. for APIs
104269 - Configurable via PHP, XML, YAML, or Doctrine Annotations
104270
104271 Installation
104272 ------------
104273 This library can be easily installed via composer
104274
104275 .. code-block :: bash
104276
104277 composer require jms/serializer
104278
104279 or just add it to your ``composer.json`` file directly.
104280
104281 Usage
104282 -----
104283 For standalone projects usage of the provided builder is encouraged::
104284
104285 $serializer = JMS\Serializer\SerializerBuilder::create()->build();
104286 $jsonContent = $serializer->serialize($data, 'json');
104287 echo $jsonContent; // or return it in a Response
104288
104289 Documentation
104290 -------------
104291
104292 .. toctree ::
104293 :hidden:
104294
104295 configuration
104296 usage
104297 event_system
104298 handlers
104299 reference
104300 cookbook
104301
104302 - :doc:`Configuration <configuration>`
104303 - :doc:`Usage <usage>`
104304 - :doc:`Events <event_system>`
104305 - :doc:`Handlers <handlers>`
104306
104307 - Recipes
104308 * :doc:`/cookbook/exclusion_strategies`
104309
104310 - Reference
104311 * :doc:`Annotations </reference/annotations>`
104312 * :doc:`XML Reference </reference/xml_reference>`
104313 * :doc:`YML Reference </reference/yml_reference>`
104314
104315 License
104316 -------
104317
104318 The code is released under the business-friendly `Apache2 license`_.
104319
104320 Documentation is subject to the `Attribution-NonCommercial-NoDerivs 3.0 Unported
104321 license`_.
104322
104323 .. _Apache2 license: http://www.apache.org/licenses/LICENSE-2.0.html
104324 .. _Attribution-NonCommercial-NoDerivs 3.0 Unported license: http://creativecommons.org/licenses/by-nc-nd/3.0/
104325
104326 Reference
104327 =========
104328
104329 .. toctree ::
104330 :glob:
104331 :maxdepth: 1
104332
104333 reference/*Cookbook
104334 ========
104335
104336 .. toctree ::
104337 :glob:
104338
104339 cookbook/*Usage
104340 =====
104341
104342 Serializing Objects
104343 -------------------
104344 Most common usage is probably to serialize objects. This can be achieved
104345 very easily:
104346
104347 .. configuration-block ::
104348
104349 .. code-block :: php
104350
104351 <?php
104352
104353 $serializer = JMS\Serializer\SerializerBuilder::create()->build();
104354 $serializer->serialize($object, 'json');
104355 $serializer->serialize($object, 'xml');
104356 $serializer->serialize($object, 'yml');
104357
104358 .. code-block :: jinja
104359
104360 {{ object | serialize }} {# uses JSON #}
104361 {{ object | serialize('json') }}
104362 {{ object | serialize('xml') }}
104363 {{ object | serialize('yml') }}
104364
104365 Deserializing Objects
104366 ---------------------
104367 You can also deserialize objects from their XML, or JSON representation. For
104368 example, when accepting data via an API.
104369
104370 .. code-block :: php
104371
104372 <?php
104373
104374 $serializer = JMS\Serializer\SerializerBuilder::create()->build();
104375 $object = $serializer->deserialize($jsonData, 'MyNamespace\MyObject', 'json');
104376
104377 Exclusion Strategies
104378 ====================
104379
104380 Introduction
104381 ------------
104382 The serializer supports different exclusion strategies. Each strategy allows
104383 you to define which properties of your objects should be serialized.
104384
104385 General Exclusion Strategies
104386 ----------------------------
104387 If you would like to always expose, or exclude certain properties. Then, you can
104388 do this with the annotations ``@ExclusionPolicy``, ``@Exclude``, and ``@Expose``.
104389
104390 The default exclusion policy is to exclude nothing. That is, all properties of the
104391 object will be serialized. If you only want to expose a few of the properties,
104392 then it is easier to change the exclusion policy, and only mark these few properties:
104393
104394 .. code-block :: php
104395
104396 <?php
104397
104398 use JMS\Serializer\Annotation\ExclusionPolicy;
104399 use JMS\Serializer\Annotation\Expose;
104400
104401 /**
104402 * The following annotations tells the serializer to skip all properties which
104403 * have not marked with @Expose.
104404 *
104405 * @ExclusionPolicy("all")
104406 */
104407 class MyObject
104408 {
104409 private $foo;
104410 private $bar;
104411
104412 /**
104413 * @Expose
104414 */
104415 private $name;
104416 }
104417
104418 .. note ::
104419
104420 A property that is excluded by ``@Exclude`` cannot be exposed anymore by any
104421 of the following strategies, but is always hidden.
104422
104423 Versioning Objects
104424 ------------------
104425 JMSSerializerBundle comes by default with a very neat feature which allows
104426 you to add versioning support to your objects, e.g. if you want to
104427 expose them via an API that is consumed by a third-party:
104428
104429 .. code-block :: php
104430
104431 <?php
104432
104433 class VersionedObject
104434 {
104435 /**
104436 * @Until("1.0.x")
104437 */
104438 private $name;
104439
104440 /**
104441 * @Since("1.1")
104442 * @SerializedName("name")
104443 */
104444 private $name2;
104445 }
104446
104447 .. note ::
104448
104449 ``@Until``, and ``@Since`` both accept a standardized PHP version number.
104450
104451 If you have annotated your objects like above, you can serializing different
104452 versions like this::
104453
104454 use JMS\Serializer\SerializationContext;
104455
104456 $serializer->serialize(new VersionObject(), 'json', SerializationContext::create()->setVersion(1));
104457
104458
104459 Creating Different Views of Your Objects
104460 ----------------------------------------
104461 Another default exclusion strategy is to create different views of your objects.
104462 Let's say you would like to serialize your object in a different view depending
104463 whether it is displayed in a list view or in a details view.
104464
104465 You can achieve that by using the ``@Groups`` annotation on your properties.
104466
104467 .. code-block :: php
104468
104469 use JMS\Serializer\Annotation\Groups;
104470
104471 class BlogPost
104472 {
104473 /** @Groups({"list", "details"}) */
104474 private $id;
104475
104476 /** @Groups({"list", "details"}) */
104477 private $title;
104478
104479 /** @Groups({"list"}) */
104480 private $nbComments;
104481
104482 /** @Groups({"details"}) */
104483 private $comments;
104484 }
104485
104486 You can then tell the serializer which groups to serialize in your controller::
104487
104488 use JMS\Serializer\SerializationContext;
104489
104490 $serializer->serialize(new BlogPost(), 'json', SerializationContext::create()->setGroups(array('list')));
104491
104492 Limiting serialization depth of some properties
104493 -----------------------------------------------
104494 You can limit the depth of what will be serialized in a property with the
104495 ``@MaxDepth`` annotation.
104496 This exclusion strategy is a bit different from the others, because it will
104497 affect the serialized content of others classes than the one you apply the
104498 annotation to.
104499
104500 .. code-block :: php
104501
104502 use JMS\Serializer\Annotation\MaxDepth;
104503
104504 class User
104505 {
104506 private $username;
104507
104508 /** @MaxDepth(1) */
104509 private $friends;
104510
104511 /** @MaxDepth(2) */
104512 private $posts;
104513 }
104514
104515 class Post
104516 {
104517 private $title;
104518
104519 private $author;
104520 }
104521
104522 In this example, serializing a user, because the max depth of the ``$friends``
104523 property is 1, the user friends would be serialized, but not their friends;
104524 and because the the max depth of the ``$posts`` property is 2, the posts would
104525 be serialized, and their author would also be serialized.
104526
104527 You need to tell the serializer to take into account MaxDepth checks::
104528
104529 use JMS\Serializer\SerializationContext;
104530
104531 $serializer->serialize($data, 'json', SerializationContext::create()->enableMaxDepthChecks());
104532 Handlers
104533 ========
104534
104535 Introduction
104536 ------------
104537 Handlers allow you to change the serialization, or deserialization process
104538 for a single type/format combination.
104539
104540 Handlers are simple callback which receive three arguments: the visitor,
104541 the data, and the type.
104542
104543 Simple Callables
104544 ----------------
104545 You can register simple callables on the builder object::
104546
104547 $builder
104548 ->configureHandlers(function(JMS\Serializer\Handler\HandlerRegistry $registry) {
104549 $registry->registerHandler('serialization', 'MyObject', 'json',
104550 function($visitor, MyObject $obj, array $type) {
104551 return $obj->getName();
104552 }
104553 );
104554 })
104555 ;
104556
104557 Subscribing Handlers
104558 --------------------
104559 Subscribing handlers contain the configuration themselves which makes them easier to share with other users,
104560 and easier to set-up in general::
104561
104562 use JMS\Serializer\Handler\SubscribingHandlerInterface;
104563 use JMS\Serializer\GraphNavigator;
104564 use JMS\Serializer\JsonSerializationVisitor;
104565 use JMS\Serializer\Context;
104566
104567 class MyHandler implements SubscribingHandlerInterface
104568 {
104569 public static function getSubscribingMethods()
104570 {
104571 return array(
104572 array(
104573 'direction' => GraphNavigator::DIRECTION_SERIALIZATION,
104574 'format' => 'json',
104575 'type' => 'DateTime',
104576 'method' => 'serializeDateTimeToJson',
104577 ),
104578 );
104579 }
104580
104581 public function serializeDateTimeToJson(JsonSerializationVisitor $visitor, \DateTime $date, array $type, Context $context)
104582 {
104583 return $date->format($type['params'][0]);
104584 }
104585 }
104586
104587 Also, this type of handler is registered via the builder object::
104588
104589 $builder
104590 ->configureHandlers(function(JMS\Serializer\Handler\HandlerRegistry $registry) {
104591 $registry->registerSubscribingHandler(new MyHandler());
104592 })
104593 ;
104594
104595 Annotations
104596 -----------
104597
104598 @ExclusionPolicy
104599 ~~~~~~~~~~~~~~~~
104600 This annotation can be defined on a class to indicate the exclusion strategy
104601 that should be used for the class.
104602
104603 +----------+----------------------------------------------------------------+
104604 | Policy | Description |
104605 +==========+================================================================+
104606 | all | all properties are excluded by default; only properties marked |
104607 | | with @Expose will be serialized/unserialized |
104608 +----------+----------------------------------------------------------------+
104609 | none | no properties are excluded by default; all properties except |
104610 | | those marked with @Exclude will be serialized/unserialized |
104611 +----------+----------------------------------------------------------------+
104612
104613 @Exclude
104614 ~~~~~~~~
104615 This annotation can be defined on a property to indicate that the property should
104616 not be serialized/unserialized. Works only in combination with NoneExclusionPolicy.
104617
104618 @Expose
104619 ~~~~~~~
104620 This annotation can be defined on a property to indicate that the property should
104621 be serialized/unserialized. Works only in combination with AllExclusionPolicy.
104622
104623 @SerializedName
104624 ~~~~~~~~~~~~~~~
104625 This annotation can be defined on a property to define the serialized name for a
104626 property. If this is not defined, the property will be translated from camel-case
104627 to a lower-cased underscored name, e.g. camelCase -> camel_case.
104628
104629 @Since
104630 ~~~~~~
104631 This annotation can be defined on a property to specify starting from which
104632 version this property is available. If an earlier version is serialized, then
104633 this property is excluded automatically. The version must be in a format that is
104634 understood by PHP's ``version_compare`` function.
104635
104636 @Until
104637 ~~~~~~
104638 This annotation can be defined on a property to specify until which version this
104639 property was available. If a later version is serialized, then this property is
104640 excluded automatically. The version must be in a format that is understood by
104641 PHP's ``version_compare`` function.
104642
104643 @Groups
104644 ~~~~~~~
104645 This annotation can be defined on a property to specifiy to if the property
104646 should be serialized when only serializing specific groups (see
104647 :doc:`../cookbook/exclusion_strategies`).
104648
104649 @MaxDepth
104650 ~~~~~~~~~
104651 This annotation can be defined on a property to limit the depth to which the
104652 content will be serialized. It is very useful when a property will contain a
104653 large object graph.
104654
104655 @AccessType
104656 ~~~~~~~~~~~
104657 This annotation can be defined on a property, or a class to specify in which way
104658 the properties should be accessed. By default, the serializer will retrieve, or
104659 set the value via reflection, but you may change this to use a public method instead:
104660
104661 .. code-block :: php
104662
104663 <?php
104664 use JMS\Serializer\Annotation\AccessType;
104665
104666 /** @AccessType("public_method") */
104667 class User
104668 {
104669 private $name;
104670
104671 public function getName()
104672 {
104673 return $this->name;
104674 }
104675
104676 public function setName($name)
104677 {
104678 $this->name = trim($name);
104679 }
104680 }
104681
104682 @Accessor
104683 ~~~~~~~~~
104684 This annotation can be defined on a property to specify which public method should
104685 be called to retrieve, or set the value of the given property:
104686
104687 .. code-block :: php
104688
104689 <?php
104690 use JMS\Serializer\Annotation\Accessor;
104691
104692 class User
104693 {
104694 private $id;
104695
104696 /** @Accessor(getter="getTrimmedName") */
104697 private $name;
104698
104699 // ...
104700 public function getTrimmedName()
104701 {
104702 return trim($this->name);
104703 }
104704
104705 public function setName($name)
104706 {
104707 $this->name = $name;
104708 }
104709 }
104710
104711 @AccessorOrder
104712 ~~~~~~~~~~~~~~
104713 This annotation can be defined on a class to control the order of properties. By
104714 default the order is undefined, but you may change it to either "alphabetical", or
104715 "custom".
104716
104717 .. code-block :: php
104718
104719 <?php
104720 use JMS\Serializer\Annotation\AccessorOrder;
104721
104722 /**
104723 * @AccessorOrder("alphabetical")
104724 *
104725 * Resulting Property Order: id, name
104726 */
104727 class User
104728 {
104729 private $id;
104730 private $name;
104731 }
104732
104733 /**
104734 * @AccessorOrder("custom", custom = {"name", "id"})
104735 *
104736 * Resulting Property Order: name, id
104737 */
104738 class User
104739 {
104740 private $id;
104741 private $name;
104742 }
104743
104744 /**
104745 * @AccessorOrder("custom", custom = {"name", "SomeMethod" ,"id"})
104746 *
104747 * Resulting Property Order: name, mood, id
104748 */
104749 class User
104750 {
104751 private $id;
104752 private $name;
104753
104754 /**
104755 * @Serializer\VirtualProperty
104756 * @Serializer\SerializedName("mood")
104757 *
104758 * @return string
104759 */
104760 public function getSomeMethod()
104761 {
104762 return 'happy';
104763 }
104764 }
104765
104766 @VirtualProperty
104767 ~~~~~~~~~~~~~~~~
104768 This annotation can be defined on a method to indicate that the data returned by
104769 the method should appear like a property of the object.
104770
104771 **Note**: This only works for serialization and is completely ignored during
104772 deserialization.
104773
104774 @Inline
104775 ~~~~~~~~
104776 This annotation can be defined on a property to indicate that the data of the property
104777 should be inlined.
104778
104779 **Note**: This only works for serialization, the serializer will not be able to deserialize
104780 objects with this annotation. Also, AccessorOrder will be using the name of the property
104781 to determine the order.
104782
104783 @ReadOnly
104784 ~~~~~~~~~
104785 This annotation can be defined on a property to indicate that the data of the property
104786 is read only and cannot be set during deserialization.
104787
104788 @PreSerialize
104789 ~~~~~~~~~~~~~
104790 This annotation can be defined on a method which is supposed to be called before
104791 the serialization of the object starts.
104792
104793 @PostSerialize
104794 ~~~~~~~~~~~~~~
104795 This annotation can be defined on a method which is then called directly after the
104796 object has been serialized.
104797
104798 @PostDeserialize
104799 ~~~~~~~~~~~~~~~~
104800 This annotation can be defined on a method which is supposed to be called after
104801 the object has been deserialized.
104802
104803 @HandlerCallback
104804 ~~~~~~~~~~~~~~~~
104805 This annotation can be defined on a method if serialization/deserialization is handled
104806 by the object iself.
104807
104808 .. code-block :: php
104809
104810 <?php
104811
104812 class Article
104813 {
104814 /**
104815 * @HandlerCallback("xml", direction = "serialization")
104816 */
104817 public function serializeToXml(XmlSerializationVisitor $visitor)
104818 {
104819 // custom logic here
104820 }
104821 }
104822
104823 @Discriminator
104824 ~~~~~~~~~~~~~~
104825
104826 .. versionadded : 0.12
104827 @Discriminator was added
104828
104829 This annotation allows deserialization of relations which are polymorphic, but
104830 where a common base class exists. The ``@Discriminator`` annotation has to be applied
104831 to the least super type::
104832
104833 /**
104834 * @Discriminator(field = "type", map = {"car": "Car", "moped": "Moped"})
104835 */
104836 abstract class Vehicle { }
104837 class Car extends Vehicle { }
104838 class Moped extends Vehicle { }
104839
104840 @Type
104841 ~~~~~
104842 This annotation can be defined on a property to specify the type of that property.
104843 For deserialization, this annotation must be defined. For serialization, you may
104844 define it in order to enhance the produced output; for example, you may want to
104845 force a certain format to be used for DateTime types.
104846
104847 Available Types:
104848
104849 +---------------------------+--------------------------------------------------+
104850 | Type | Description |
104851 +===========================+==================================================+
104852 | boolean | Primitive boolean |
104853 +---------------------------+--------------------------------------------------+
104854 | integer | Primitive integer |
104855 +---------------------------+--------------------------------------------------+
104856 | double | Primitive double |
104857 +---------------------------+--------------------------------------------------+
104858 | string | Primitive string |
104859 +---------------------------+--------------------------------------------------+
104860 | array | An array with arbitrary keys, and values. |
104861 +---------------------------+--------------------------------------------------+
104862 | array<T> | A list of type T (T can be any available type). |
104863 | | Examples: |
104864 | | array<string>, array<MyNamespace\MyObject>, etc. |
104865 +---------------------------+--------------------------------------------------+
104866 | array<K, V> | A map of keys of type K to values of type V. |
104867 | | Examples: array<string, string>, |
104868 | | array<string, MyNamespace\MyObject>, etc. |
104869 +---------------------------+--------------------------------------------------+
104870 | DateTime | PHP's DateTime object (default format/timezone) |
104871 +---------------------------+--------------------------------------------------+
104872 | DateTime<'format'> | PHP's DateTime object (custom format/default |
104873 | | timezone) |
104874 +---------------------------+--------------------------------------------------+
104875 | DateTime<'format', 'zone'>| PHP's DateTime object (custom format/timezone) |
104876 +---------------------------+--------------------------------------------------+
104877 | T | Where T is a fully qualified class name. |
104878 +---------------------------+--------------------------------------------------+
104879 | ArrayCollection<T> | Similar to array<T>, but will be deserialized |
104880 | | into Doctrine's ArrayCollection class. |
104881 +---------------------------+--------------------------------------------------+
104882 | ArrayCollection<K, V> | Similar to array<K, V>, but will be deserialized |
104883 | | into Doctrine's ArrayCollection class. |
104884 +---------------------------+--------------------------------------------------+
104885
104886 Examples:
104887
104888 .. code-block :: php
104889
104890 <?php
104891
104892 namespace MyNamespace;
104893
104894 use JMS\Serializer\Annotation\Type;
104895
104896 class BlogPost
104897 {
104898 /**
104899 * @Type("ArrayCollection<MyNamespace\Comment>")
104900 */
104901 private $comments;
104902
104903 /**
104904 * @Type("string")
104905 */
104906 private $title;
104907
104908 /**
104909 * @Type("MyNamespace\Author")
104910 */
104911 private $author;
104912
104913 /**
104914 * @Type("DateTime")
104915 */
104916 private $createdAt;
104917
104918 /**
104919 * @Type("DateTime<'Y-m-d'>")
104920 */
104921 private $updatedAt;
104922
104923 /**
104924 * @Type("boolean")
104925 */
104926 private $published;
104927
104928 /**
104929 * @Type("array<string, string>")
104930 */
104931 private $keyValueStore;
104932 }
104933
104934 @XmlRoot
104935 ~~~~~~~~
104936 This allows you to specify the name of the top-level element.
104937
104938 .. code-block :: php
104939
104940 <?php
104941
104942 use JMS\Serializer\Annotation\XmlRoot;
104943
104944 /** @XmlRoot("user") */
104945 class User
104946 {
104947 private $name = 'Johannes';
104948 }
104949
104950 Resulting XML:
104951
104952 .. code-block :: xml
104953
104954 <user>
104955 <name><![CDATA[Johannes]]></name>
104956 </user>
104957
104958 .. note ::
104959
104960 @XmlRoot only applies to the root element, but is for example not taken into
104961 account for collections. You can define the entry name for collections using
104962 @XmlList, or @XmlMap.
104963
104964 @XmlAttribute
104965 ~~~~~~~~~~~~~
104966 This allows you to mark properties which should be set as attributes,
104967 and not as child elements.
104968
104969 .. code-block :: php
104970
104971 <?php
104972
104973 use JMS\Serializer\Annotation\XmlAttribute;
104974
104975 class User
104976 {
104977 /** @XmlAttribute */
104978 private $id = 1;
104979 private $name = 'Johannes';
104980 }
104981
104982 Resulting XML:
104983
104984 .. code-block :: xml
104985
104986 <result id="1">
104987 <name><![CDATA[Johannes]]></name>
104988 </result>
104989
104990 @XmlValue
104991 ~~~~~~~~~
104992 This allows you to mark properties which should be set as the value of the
104993 current element. Note that this has the limitation that any additional
104994 properties of that object must have the @XmlAttribute annotation.
104995
104996 .. code-block :: php
104997
104998 <?php
104999
105000 use JMS\Serializer\Annotation\XmlAttribute;
105001 use JMS\Serializer\Annotation\XmlValue;
105002 use JMS\Serializer\Annotation\XmlRoot;
105003
105004 /** @XmlRoot("price") */
105005 class Price
105006 {
105007 /** @XmlAttribute */
105008 private $currency = 'EUR';
105009
105010 /** @XmlValue */
105011 private $amount = 1.23;
105012 }
105013
105014 Resulting XML:
105015
105016 .. code-block :: xml
105017
105018 <price currency="EUR">1.23</price>
105019
105020 @XmlList
105021 ~~~~~~~~
105022 This allows you to define several properties of how arrays should be
105023 serialized. This is very similar to @XmlMap, and should be used if the
105024 keys of the array are not important.
105025
105026 .. code-block :: php
105027
105028 <?php
105029
105030 use JMS\Serializer\Annotation\XmlList;
105031 use JMS\Serializer\Annotation\XmlRoot;
105032
105033 /** @XmlRoot("post") */
105034 class Post
105035 {
105036 /**
105037 * @XmlList(inline = true, entry = "comment")
105038 */
105039 private $comments = array(
105040 new Comment('Foo'),
105041 new Comment('Bar'),
105042 );
105043 }
105044
105045 class Comment
105046 {
105047 private $text;
105048
105049 public function __construct($text)
105050 {
105051 $this->text = $text;
105052 }
105053 }
105054
105055 Resulting XML:
105056
105057 .. code-block :: xml
105058
105059 <post>
105060 <comment>
105061 <text><![CDATA[Foo]]></text>
105062 </comment>
105063 <comment>
105064 <text><![CDATA[Bar]]></text>
105065 </comment>
105066 </post>
105067
105068 @XmlMap
105069 ~~~~~~~
105070 Similar to @XmlList, but the keys of the array are meaningful.
105071
105072 @XmlKeyValuePairs
105073 ~~~~~~~~~~~~~~~~~
105074 This allows you to use the keys of an array as xml tags.
105075
105076 .. note ::
105077
105078 When a key is an invalid xml tag name (e.g. 1_foo) the tag name *entry* will be used instead of the key.
105079
105080 @XmlAttributeMap
105081 ~~~~~~~~~~~~~~~~
105082
105083 This is similar to the @XmlKeyValuePairs, but instead of creating child elements, it creates attributes.
105084
105085 .. code-block :: php
105086
105087 <?php
105088
105089 use JMS\Serializer\Annotation\XmlAttribute;
105090
105091 class Input
105092 {
105093 /** @XmlAttributeMap */
105094 private $id = array(
105095 'name' => 'firstname',
105096 'value' => 'Adrien',
105097 );
105098 }
105099
105100 Resulting XML:
105101
105102 .. code-block :: xml
105103
105104 <result name="firstname" value="Adrien"/>
105105 YAML Reference
105106 --------------
105107 ::
105108
105109 # MyBundle\Resources\config\serializer\ClassName.yml
105110 Fully\Qualified\ClassName:
105111 exclusion_policy: ALL
105112 xml_root_name: foobar
105113 exclude: true
105114 access_type: public_method # defaults to property
105115 accessor_order: custom
105116 custom_accessor_order: [propertyName1, propertyName2, ..., propertyNameN]
105117 discriminator:
105118 field_name: type
105119 map:
105120 some-value: ClassName
105121 properties:
105122 some-property:
105123 exclude: true
105124 expose: true
105125 access_type: public_method # defaults to property
105126 accessor: # access_type must be set to public_method
105127 getter: getSomeOtherProperty
105128 setter: setSomeOtherProperty
105129 type: string
105130 serialized_name: foo
105131 since_version: 1.0
105132 until_version: 1.1
105133 groups: [foo, bar]
105134 xml_attribute: true
105135 xml_value: true
105136 inline: true
105137 read_only: true
105138 xml_key_value_pairs: true
105139 xml_list:
105140 inline: true
105141 entry_name: foo
105142 xml_map:
105143 inline: true
105144 key_attribute_name: foo
105145 entry_name: bar
105146 xml_attribute_map: true
105147 max_depth: 2
105148
105149 handler_callbacks:
105150 serialization:
105151 xml: serializeToXml
105152 json: serializeToJson
105153 deserialization:
105154 xml: deserializeFromXml
105155
105156 callback_methods:
105157 pre_serialize: [foo, bar]
105158 post_serialize: [foo, bar]
105159 post_deserialize: [foo, bar]
105160 XML Reference
105161 -------------
105162 ::
105163
105164 <!-- MyBundle\Resources\config\serializer\ClassName.xml -->
105165 <?xml version="1.0" encoding="UTF-8" ?>
105166 <serializer>
105167 <class name="Fully\Qualified\ClassName" exclusion-policy="ALL" xml-root-name="foo-bar" exclude="true"
105168 accessor-order="custom" custom-accessor-order="propertyName1,propertyName2,...,propertyNameN"
105169 access-type="public_method" discriminator-field-name="type">
105170 <discriminator-class value="some-value">ClassName</discriminator-class>
105171 <property name="some-property"
105172 exclude="true"
105173 expose="true"
105174 type="string"
105175 serialized-name="foo"
105176 since-version="1.0"
105177 until-version="1.1"
105178 xml-attribute="true"
105179 access-type="public_method"
105180 accessor-getter="getSomeProperty"
105181 accessor-setter="setSomeProperty"
105182 inline="true"
105183 read-only="true"
105184 groups="foo,bar"
105185 xml-key-value-pairs="true"
105186 xml-attribute-map="true"
105187 max-depth="2"
105188 >
105189 <!-- You can also specify the type as element which is necessary if
105190 your type contains "<" or ">" characters. -->
105191 <type><![CDATA[]]></type>
105192 <xml-list inline="true" entry-name="foobar" />
105193 <xml-map inline="true" key-attribute-name="foo" entry-name="bar" />
105194 </property>
105195 <callback-method name="foo" type="pre-serialize" />
105196 <callback-method name="bar" type="post-serialize" />
105197 <callback-method name="baz" type="post-deserialize" />
105198 <callback-method name="serializeToXml" type="handler" direction="serialization" format="xml" />
105199 <callback-method name="deserializeFromJson" type="handler" direction="deserialization" format="xml" />
105200 </class>
105201 </serializer>
105202 Configuration
105203 =============
105204
105205 .. note ::
105206
105207 If you are using Symfony2, this section is mostly irrelevant for you as the entire integration is provided by
105208 JMSSerializerBundle; please see `its documentation <http://jmsyst.com/bundles/JMSSerializerBundle>`_. If you are
105209 using another framework, there also might be a module, or other special integration. Please check packagist, or
105210 whatever registry usually holds such information for your framework.
105211
105212 Constructing a Serializer
105213 -------------------------
105214
105215 This library provides a special builder object which makes constructing serializer instances a breeze in any PHP
105216 project. In its shortest version, it's just a single line of code::
105217
105218 $serializer = JMS\Serializer\SerializerBuilder::create()->build();
105219
105220 This serializer is fully functional, but you might want to tweak it a bit for example to configure a cache directory.
105221
105222 Configuring a Cache Directory
105223 -----------------------------
105224 The serializer collects several metadata about your objects from various sources such as YML, XML, or annotations. In
105225 order to make this process as efficient as possible, it is encourage to let the serializer cache that information. For
105226 that, you can configure a cache directory::
105227
105228 $builder = new JMS\Serializer\SerializerBuilder();
105229
105230 $serializer =
105231 JMS\Serializer\SerializerBuilder::create()
105232 ->setCacheDir($someWritableDir)
105233 ->setDebug($trueOrFalse)
105234 ->build();
105235
105236 As you can see, we also added a call to the ``setDebug`` method. In debug mode, the serializer will perform a bit more
105237 filesystem checks to see whether the data that it has cached is still valid. These checks are useful during development
105238 so that you do not need to manually clear cache folders, however in production they are just unnecessary overhead. The
105239 debug setting allows you to make the behavior environment specific.
105240
105241 Adding Custom Handlers
105242 ----------------------
105243 If you have created custom handlers, you can add them to the serializer easily::
105244
105245 $serializer =
105246 JMS\Serializer\SerializerBuilder::create()
105247 ->addDefaultHandlers()
105248 ->configureHandlers(function(JMS\Serializer\Handler\HandlerRegistry $registry) {
105249 $registry->registerHandler('serialization', 'MyObject', 'json',
105250 function($visitor, MyObject $obj, array $type) {
105251 return $obj->getName();
105252 }
105253 );
105254 })
105255 ->build();
105256
105257 For more complex handlers, it is advisable to extract them to dedicated classes,
105258 see :doc:`handlers documentation <handlers>`.
105259
105260 Configuring Metadata Locations
105261 ------------------------------
105262 This library supports several metadata sources. By default, it uses Doctrine annotations, but you may also store
105263 metadata in XML, or YML files. For the latter, it is necessary to configure a metadata directory where those files
105264 are located::
105265
105266 $serializer =
105267 JMS\Serializer\SerializerBuilder::create()
105268 ->addMetadataDir($someDir)
105269 ->build();
105270
105271 The serializer would expect the metadata files to be named like the fully qualified class names where all ``\`` are
105272 replaced with ``.``. So, if you class would be named ``Vendor\Package\Foo``, the metadata file would need to be located
105273 at ``$someDir/Vendor.Package.Foo.(xml|yml)``. For more information, see the :doc:`reference <reference>`.
105274 <?php
105275
105276 if ( ! isset($_SERVER['argv'][1], $_SERVER['argv'][2])) {
105277 echo 'Usage: php benchmark.php <format> <iterations> [output-file]'.PHP_EOL;
105278 exit(1);
105279 }
105280
105281 list(, $format, $iterations) = $_SERVER['argv'];
105282
105283 require_once 'bootstrap.php';
105284
105285 function benchmark(\Closure $f, $times = 10) {
105286 $time = microtime(true);
105287 for ($i=0; $i<$times; $i++) {
105288 $f();
105289 }
105290
105291 return (microtime(true) - $time) / $times;
105292 }
105293
105294 function createCollection() {
105295 $collection = array();
105296 for ($i=0; $i<50; $i++) {
105297 $collection[] = createObject();
105298 }
105299
105300 return $collection;
105301 }
105302
105303 function createObject() {
105304 $post = new \JMS\Serializer\Tests\Fixtures\BlogPost('FooooooooooooooooooooooBAR', new \JMS\Serializer\Tests\Fixtures\Author('Foo'), new \DateTime);
105305 for ($i=0; $i<10; $i++) {
105306 $post->addComment(new \JMS\Serializer\Tests\Fixtures\Comment(new \JMS\Serializer\Tests\Fixtures\Author('foo'), 'foobar'));
105307 }
105308
105309 return $post;
105310 }
105311
105312
105313
105314 $serializer = \JMS\Serializer\SerializerBuilder::create()->build();
105315 $collection = createCollection();
105316 $metrics = array();
105317 $f = function() use ($serializer, $collection, $format) {
105318 $serializer->serialize($collection, $format);
105319 };
105320
105321
105322 // Load all necessary classes into memory.
105323 benchmark($f, 1);
105324
105325 printf('Benchmarking collection for format "%s".'.PHP_EOL, $format);
105326 $metrics['benchmark-collection-'.$format] = benchmark($f, $iterations);
105327
105328 $output = json_encode(array('metrics' => $metrics));
105329
105330 if (isset($_SERVER['argv'][3])) {
105331 file_put_contents($_SERVER['argv'][3], $output);
105332 echo "Done.".PHP_EOL;
105333 } else {
105334 echo $output.PHP_EOL;
105335 }
105336 <?php
105337
105338 /*
105339 * Copyright 2011 Johannes M. Schmitt <schmittjoh@gmail.com>
105340 *
105341 * Licensed under the Apache License, Version 2.0 (the "License");
105342 * you may not use this file except in compliance with the License.
105343 * You may obtain a copy of the License at
105344 *
105345 * http://www.apache.org/licenses/LICENSE-2.0
105346 *
105347 * Unless required by applicable law or agreed to in writing, software
105348 * distributed under the License is distributed on an "AS IS" BASIS,
105349 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
105350 * See the License for the specific language governing permissions and
105351 * limitations under the License.
105352 */
105353
105354 use Doctrine\Common\Annotations\AnnotationRegistry;
105355
105356 call_user_func(function() {
105357 if ( ! is_file($autoloadFile = __DIR__.'/../vendor/autoload.php')) {
105358 throw new \RuntimeException('Did not find vendor/autoload.php. Did you run "composer install --dev"?');
105359 }
105360
105361 $loader = require $autoloadFile;
105362 $loader->add('JMS\Serializer\Tests', __DIR__);
105363
105364 AnnotationRegistry::registerLoader('class_exists');
105365 });
105366 <?php
105367
105368 /*
105369 * Copyright 2013 Johannes M. Schmitt <schmittjoh@gmail.com>
105370 *
105371 * Licensed under the Apache License, Version 2.0 (the "License");
105372 * you may not use this file except in compliance with the License.
105373 * You may obtain a copy of the License at
105374 *
105375 * http://www.apache.org/licenses/LICENSE-2.0
105376 *
105377 * Unless required by applicable law or agreed to in writing, software
105378 * distributed under the License is distributed on an "AS IS" BASIS,
105379 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
105380 * See the License for the specific language governing permissions and
105381 * limitations under the License.
105382 */
105383
105384 namespace JMS\Serializer\Tests\Fixtures;
105385
105386 use JMS\Serializer\Annotation\XmlRoot;
105387 use JMS\Serializer\Annotation\SerializedName;
105388 use JMS\Serializer\Annotation\XmlList;
105389 use JMS\Serializer\Annotation\XmlMap;
105390 use JMS\Serializer\Annotation\Type;
105391
105392 /** @XmlRoot("log") */
105393 class Log
105394 {
105395 /**
105396 * @SerializedName("author_list")
105397 * @XmlMap
105398 * @Type("AuthorList")
105399 */
105400 private $authors;
105401
105402 /**
105403 * @XmlList(inline=true, entry = "comment")
105404 * @Type("array<JMS\Serializer\Tests\Fixtures\Comment>")
105405 */
105406 private $comments;
105407
105408 public function __construct()
105409 {
105410 $this->authors = new AuthorList();
105411 $this->authors->add(new Author('Johannes Schmitt'));
105412 $this->authors->add(new Author('John Doe'));
105413
105414 $author = new Author('Foo Bar');
105415 $this->comments = array();
105416 $this->comments[] = new Comment($author, 'foo');
105417 $this->comments[] = new Comment($author, 'bar');
105418 $this->comments[] = new Comment($author, 'baz');
105419 }
105420 }<?php
105421
105422 /*
105423 * Copyright 2013 Johannes M. Schmitt <schmittjoh@gmail.com>
105424 *
105425 * Licensed under the Apache License, Version 2.0 (the "License");
105426 * you may not use this file except in compliance with the License.
105427 * You may obtain a copy of the License at
105428 *
105429 * http://www.apache.org/licenses/LICENSE-2.0
105430 *
105431 * Unless required by applicable law or agreed to in writing, software
105432 * distributed under the License is distributed on an "AS IS" BASIS,
105433 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
105434 * See the License for the specific language governing permissions and
105435 * limitations under the License.
105436 */
105437
105438 namespace JMS\Serializer\Tests\Fixtures;
105439
105440 use JMS\Serializer\Annotation as Serializer;
105441
105442 /** @Serializer\XmlRoot("price") */
105443 class CurrencyAwarePrice
105444 {
105445 /**
105446 * @Serializer\XmlAttribute
105447 * @Serializer\Type("string")
105448 */
105449 private $currency;
105450
105451 /**
105452 * @Serializer\XmlValue
105453 * @Serializer\Type("double")
105454 */
105455 private $amount;
105456
105457 public function __construct($amount, $currency = 'EUR')
105458 {
105459 $this->currency = $currency;
105460 $this->amount = $amount;
105461 }
105462 }<?php
105463
105464 /*
105465 * Copyright 2013 Johannes M. Schmitt <schmittjoh@gmail.com>
105466 *
105467 * Licensed under the Apache License, Version 2.0 (the "License");
105468 * you may not use this file except in compliance with the License.
105469 * You may obtain a copy of the License at
105470 *
105471 * http://www.apache.org/licenses/LICENSE-2.0
105472 *
105473 * Unless required by applicable law or agreed to in writing, software
105474 * distributed under the License is distributed on an "AS IS" BASIS,
105475 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
105476 * See the License for the specific language governing permissions and
105477 * limitations under the License.
105478 */
105479
105480 namespace JMS\Serializer\Tests\Fixtures;
105481
105482 use JMS\Serializer\Annotation\SerializedName;
105483 use JMS\Serializer\Annotation\Since;
105484 use JMS\Serializer\Annotation\Until;
105485
105486 class VersionedObject
105487 {
105488 /**
105489 * @Until("1.0.0")
105490 */
105491 private $name;
105492
105493 /**
105494 * @Since("1.0.1")
105495 * @SerializedName("name")
105496 */
105497 private $name2;
105498
105499 public function __construct($name, $name2)
105500 {
105501 $this->name = $name;
105502 $this->name2 = $name2;
105503 }
105504 }<?php
105505
105506 /*
105507 * Copyright 2013 Johannes M. Schmitt <schmittjoh@gmail.com>
105508 *
105509 * Licensed under the Apache License, Version 2.0 (the "License");
105510 * you may not use this file except in compliance with the License.
105511 * You may obtain a copy of the License at
105512 *
105513 * http://www.apache.org/licenses/LICENSE-2.0
105514 *
105515 * Unless required by applicable law or agreed to in writing, software
105516 * distributed under the License is distributed on an "AS IS" BASIS,
105517 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
105518 * See the License for the specific language governing permissions and
105519 * limitations under the License.
105520 */
105521
105522 namespace JMS\Serializer\Tests\Fixtures;
105523
105524 use JMS\Serializer\Annotation\VirtualProperty;
105525 use JMS\Serializer\Annotation\ExclusionPolicy;
105526
105527 /**
105528 * @ExclusionPolicy("all")
105529 */
105530 class ObjectWithVirtualPropertiesAndExcludeAll
105531 {
105532 /**
105533 * @VirtualProperty
105534 */
105535 public function getVirtualValue()
105536 {
105537 return 'value';
105538 }
105539 }<?php
105540
105541 /*
105542 * Copyright 2013 Johannes M. Schmitt <schmittjoh@gmail.com>
105543 *
105544 * Licensed under the Apache License, Version 2.0 (the "License");
105545 * you may not use this file except in compliance with the License.
105546 * You may obtain a copy of the License at
105547 *
105548 * http://www.apache.org/licenses/LICENSE-2.0
105549 *
105550 * Unless required by applicable law or agreed to in writing, software
105551 * distributed under the License is distributed on an "AS IS" BASIS,
105552 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
105553 * See the License for the specific language governing permissions and
105554 * limitations under the License.
105555 */
105556
105557 namespace JMS\Serializer\Tests\Fixtures;
105558
105559 use JMS\Serializer\Annotation\XmlRoot;
105560 use JMS\Serializer\Annotation\Type;
105561
105562 /**
105563 * @XmlRoot("person_location")
105564 */
105565 class PersonLocation
105566 {
105567 /**
105568 * @Type("JMS\Serializer\Tests\Fixtures\Person")
105569 */
105570 public $person;
105571
105572 /**
105573 * @Type("string")
105574 */
105575 public $location;
105576 }
105577 <?php
105578
105579 /*
105580 * Copyright 2013 Johannes M. Schmitt <schmittjoh@gmail.com>
105581 *
105582 * Licensed under the Apache License, Version 2.0 (the "License");
105583 * you may not use this file except in compliance with the License.
105584 * You may obtain a copy of the License at
105585 *
105586 * http://www.apache.org/licenses/LICENSE-2.0
105587 *
105588 * Unless required by applicable law or agreed to in writing, software
105589 * distributed under the License is distributed on an "AS IS" BASIS,
105590 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
105591 * See the License for the specific language governing permissions and
105592 * limitations under the License.
105593 */
105594
105595 namespace JMS\Serializer\Tests\Fixtures;
105596
105597 use Doctrine\Common\Collections\ArrayCollection;
105598 use JMS\Serializer\Annotation\Type;
105599 use JMS\Serializer\Annotation\PostDeserialize;
105600
105601 /** No annotation */
105602 class CircularReferenceParent
105603 {
105604 /** @Type("array<JMS\Serializer\Tests\Fixtures\CircularReferenceChild>") */
105605 protected $collection = array();
105606
105607 /** @Type("ArrayCollection<JMS\Serializer\Tests\Fixtures\CircularReferenceChild>") */
105608 private $anotherCollection;
105609
105610 public function __construct()
105611 {
105612 $this->collection[] = new CircularReferenceChild('child1', $this);
105613 $this->collection[] = new CircularReferenceChild('child2', $this);
105614
105615 $this->anotherCollection = new ArrayCollection();
105616 $this->anotherCollection->add(new CircularReferenceChild('child1', $this));
105617 $this->anotherCollection->add(new CircularReferenceChild('child2', $this));
105618 }
105619
105620 /** @PostDeserialize */
105621 private function afterDeserialization()
105622 {
105623 if (!$this->collection) {
105624 $this->collection = array();
105625 }
105626 foreach ($this->collection as $v) {
105627 $v->setParent($this);
105628 }
105629
105630 if (!$this->anotherCollection) {
105631 $this->anotherCollection = new ArrayCollection();
105632 }
105633 foreach ($this->anotherCollection as $v) {
105634 $v->setParent($this);
105635 }
105636 }
105637 }<?php
105638
105639 /*
105640 * Copyright 2013 Johannes M. Schmitt <schmittjoh@gmail.com>
105641 *
105642 * Licensed under the Apache License, Version 2.0 (the "License");
105643 * you may not use this file except in compliance with the License.
105644 * You may obtain a copy of the License at
105645 *
105646 * http://www.apache.org/licenses/LICENSE-2.0
105647 *
105648 * Unless required by applicable law or agreed to in writing, software
105649 * distributed under the License is distributed on an "AS IS" BASIS,
105650 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
105651 * See the License for the specific language governing permissions and
105652 * limitations under the License.
105653 */
105654
105655 namespace JMS\Serializer\Tests\Fixtures;
105656
105657 use JMS\Serializer\Annotation\XmlRoot;
105658 use JMS\Serializer\Annotation\XmlList;
105659 use Doctrine\Common\Collections\ArrayCollection;
105660 use JMS\Serializer\Annotation\Type;
105661
105662 /**
105663 * @XmlRoot("person_collection")
105664 */
105665 class PersonCollection
105666 {
105667 /**
105668 * @Type("ArrayCollection<JMS\Serializer\Tests\Fixtures\Person>")
105669 * @XmlList(entry = "person", inline = true)
105670 */
105671 public $persons;
105672
105673 /**
105674 * @Type("string")
105675 */
105676 public $location;
105677
105678 public function __construct()
105679 {
105680 $this->persons = new ArrayCollection;
105681 }
105682 }
105683 <?php
105684
105685 /*
105686 * Copyright 2013 Johannes M. Schmitt <schmittjoh@gmail.com>
105687 *
105688 * Licensed under the Apache License, Version 2.0 (the "License");
105689 * you may not use this file except in compliance with the License.
105690 * You may obtain a copy of the License at
105691 *
105692 * http://www.apache.org/licenses/LICENSE-2.0
105693 *
105694 * Unless required by applicable law or agreed to in writing, software
105695 * distributed under the License is distributed on an "AS IS" BASIS,
105696 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
105697 * See the License for the specific language governing permissions and
105698 * limitations under the License.
105699 */
105700
105701 namespace JMS\Serializer\Tests\Fixtures;
105702
105703 use Doctrine\Common\Collections\ArrayCollection;
105704
105705 use JMS\Serializer\Metadata\ClassMetadata;
105706
105707 use JMS\Serializer\Construction\ObjectConstructorInterface;
105708 use JMS\Serializer\VisitorInterface;
105709
105710 use JMS\Serializer\Tests\Fixtures\Author;
105711 use JMS\Serializer\Construction\UnserializeObjectConstructor;
105712
105713 class InitializedObjectConstructor extends UnserializeObjectConstructor
105714 {
105715 public function construct(VisitorInterface $visitor, ClassMetadata $metadata, $data, array $type)
105716 {
105717 if ($type['name'] !== 'JMS\Serializer\Tests\Fixtures\BlogPost') {
105718 return parent::construct($visitor, $metadata, $data, $type);
105719 }
105720
105721 return new BlogPost('This is a nice title.', new Author('Foo Bar'), new \DateTime('2011-07-30 00:00', new \DateTimeZone('UTC')));
105722 }
105723 }
105724 <?php
105725
105726 /*
105727 * Copyright 2013 Johannes M. Schmitt <schmittjoh@gmail.com>
105728 *
105729 * Licensed under the Apache License, Version 2.0 (the "License");
105730 * you may not use this file except in compliance with the License.
105731 * You may obtain a copy of the License at
105732 *
105733 * http://www.apache.org/licenses/LICENSE-2.0
105734 *
105735 * Unless required by applicable law or agreed to in writing, software
105736 * distributed under the License is distributed on an "AS IS" BASIS,
105737 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
105738 * See the License for the specific language governing permissions and
105739 * limitations under the License.
105740 */
105741
105742 namespace JMS\Serializer\Tests\Fixtures;
105743
105744 use JMS\Serializer\Annotation as Serializer;
105745
105746 /** @Serializer\AccessorOrder("custom", custom = {"c", "d", "a", "b"}) */
105747 class AccessorOrderChild extends AccessorOrderParent
105748 {
105749 private $c = 'c', $d = 'd';
105750 }<?php
105751
105752 /*
105753 * Copyright 2013 Johannes M. Schmitt <schmittjoh@gmail.com>
105754 *
105755 * Licensed under the Apache License, Version 2.0 (the "License");
105756 * you may not use this file except in compliance with the License.
105757 * You may obtain a copy of the License at
105758 *
105759 * http://www.apache.org/licenses/LICENSE-2.0
105760 *
105761 * Unless required by applicable law or agreed to in writing, software
105762 * distributed under the License is distributed on an "AS IS" BASIS,
105763 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
105764 * See the License for the specific language governing permissions and
105765 * limitations under the License.
105766 */
105767
105768 namespace JMS\Serializer\Tests\Fixtures;
105769
105770 use JMS\Serializer\Annotation as Serializer;
105771 use JMS\Serializer\Annotation\Type;
105772
105773 class InlineChild
105774 {
105775 /**
105776 * @Type("string")
105777 */
105778 private $a = 'a';
105779
105780 /**
105781 * @Type("string")
105782 */
105783 private $b = 'b';
105784 }<?php
105785
105786 /*
105787 * Copyright 2013 Johannes M. Schmitt <schmittjoh@gmail.com>
105788 *
105789 * Licensed under the Apache License, Version 2.0 (the "License");
105790 * you may not use this file except in compliance with the License.
105791 * You may obtain a copy of the License at
105792 *
105793 * http://www.apache.org/licenses/LICENSE-2.0
105794 *
105795 * Unless required by applicable law or agreed to in writing, software
105796 * distributed under the License is distributed on an "AS IS" BASIS,
105797 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
105798 * See the License for the specific language governing permissions and
105799 * limitations under the License.
105800 */
105801
105802 namespace JMS\Serializer\Tests\Fixtures\Discriminator;
105803
105804 class Moped extends Vehicle
105805 {
105806 }<?php
105807
105808 /*
105809 * Copyright 2013 Johannes M. Schmitt <schmittjoh@gmail.com>
105810 *
105811 * Licensed under the Apache License, Version 2.0 (the "License");
105812 * you may not use this file except in compliance with the License.
105813 * You may obtain a copy of the License at
105814 *
105815 * http://www.apache.org/licenses/LICENSE-2.0
105816 *
105817 * Unless required by applicable law or agreed to in writing, software
105818 * distributed under the License is distributed on an "AS IS" BASIS,
105819 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
105820 * See the License for the specific language governing permissions and
105821 * limitations under the License.
105822 */
105823
105824 namespace JMS\Serializer\Tests\Fixtures\Discriminator;
105825
105826 class Car extends Vehicle
105827 {
105828 }<?php
105829
105830 /*
105831 * Copyright 2013 Johannes M. Schmitt <schmittjoh@gmail.com>
105832 *
105833 * Licensed under the Apache License, Version 2.0 (the "License");
105834 * you may not use this file except in compliance with the License.
105835 * You may obtain a copy of the License at
105836 *
105837 * http://www.apache.org/licenses/LICENSE-2.0
105838 *
105839 * Unless required by applicable law or agreed to in writing, software
105840 * distributed under the License is distributed on an "AS IS" BASIS,
105841 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
105842 * See the License for the specific language governing permissions and
105843 * limitations under the License.
105844 */
105845
105846 namespace JMS\Serializer\Tests\Fixtures\Discriminator;
105847
105848 use JMS\Serializer\Annotation as Serializer;
105849
105850 /**
105851 * @Serializer\Discriminator(field = "type", map = {
105852 * "car": "JMS\Serializer\Tests\Fixtures\Discriminator\Car",
105853 * "moped": "JMS\Serializer\Tests\Fixtures\Discriminator\Moped",
105854 * })
105855 */
105856 abstract class Vehicle
105857 {
105858 /** @Serializer\Type("integer") */
105859 public $km;
105860
105861 public function __construct($km)
105862 {
105863 $this->km = (integer) $km;
105864 }
105865 }<?php
105866
105867 /*
105868 * Copyright 2013 Johannes M. Schmitt <schmittjoh@gmail.com>
105869 *
105870 * Licensed under the Apache License, Version 2.0 (the "License");
105871 * you may not use this file except in compliance with the License.
105872 * You may obtain a copy of the License at
105873 *
105874 * http://www.apache.org/licenses/LICENSE-2.0
105875 *
105876 * Unless required by applicable law or agreed to in writing, software
105877 * distributed under the License is distributed on an "AS IS" BASIS,
105878 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
105879 * See the License for the specific language governing permissions and
105880 * limitations under the License.
105881 */
105882
105883 namespace JMS\Serializer\Tests\Fixtures;
105884
105885 use JMS\Serializer\Annotation\Expose;
105886 use JMS\Serializer\Annotation\ExclusionPolicy;
105887
105888 /**
105889 * @ExclusionPolicy("all")
105890 *
105891 * @author Johannes M. Schmitt <schmittjoh@gmail.com>
105892 */
105893 class AllExcludedObject
105894 {
105895 private $foo = 'foo';
105896
105897 /**
105898 * @Expose
105899 */
105900 private $bar = 'bar';
105901 }<?php
105902
105903 /*
105904 * Copyright 2013 Johannes M. Schmitt <schmittjoh@gmail.com>
105905 *
105906 * Licensed under the Apache License, Version 2.0 (the "License");
105907 * you may not use this file except in compliance with the License.
105908 * You may obtain a copy of the License at
105909 *
105910 * http://www.apache.org/licenses/LICENSE-2.0
105911 *
105912 * Unless required by applicable law or agreed to in writing, software
105913 * distributed under the License is distributed on an "AS IS" BASIS,
105914 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
105915 * See the License for the specific language governing permissions and
105916 * limitations under the License.
105917 */
105918
105919 namespace JMS\Serializer\Tests\Fixtures;
105920
105921 use JMS\Serializer\Annotation as Serializer;
105922
105923 /**
105924 * @Serializer\XmlRoot("input")
105925 */
105926 class Input
105927 {
105928 /**
105929 * @Serializer\XmlAttributeMap
105930 */
105931 private $attributes;
105932
105933 public function __construct($attributes = null)
105934 {
105935 $this->attributes = $attributes ?: array(
105936 'type' => 'text',
105937 'name' => 'firstname',
105938 'value' => 'Adrien',
105939 );
105940 }
105941 }
105942 <?php
105943
105944 /*
105945 * Copyright 2013 Johannes M. Schmitt <schmittjoh@gmail.com>
105946 *
105947 * Licensed under the Apache License, Version 2.0 (the "License");
105948 * you may not use this file except in compliance with the License.
105949 * You may obtain a copy of the License at
105950 *
105951 * http://www.apache.org/licenses/LICENSE-2.0
105952 *
105953 * Unless required by applicable law or agreed to in writing, software
105954 * distributed under the License is distributed on an "AS IS" BASIS,
105955 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
105956 * See the License for the specific language governing permissions and
105957 * limitations under the License.
105958 */
105959
105960 namespace JMS\Serializer\Tests\Fixtures;
105961
105962 use JMS\Serializer\Annotation as Serializer;
105963
105964 class ObjectWithEmptyHash
105965 {
105966 /**
105967 * @Serializer\Type("array<string,string>")
105968 */
105969 private $hash = array();
105970 }
105971 <?php
105972
105973 /*
105974 * Copyright 2013 Johannes M. Schmitt <schmittjoh@gmail.com>
105975 *
105976 * Licensed under the Apache License, Version 2.0 (the "License");
105977 * you may not use this file except in compliance with the License.
105978 * You may obtain a copy of the License at
105979 *
105980 * http://www.apache.org/licenses/LICENSE-2.0
105981 *
105982 * Unless required by applicable law or agreed to in writing, software
105983 * distributed under the License is distributed on an "AS IS" BASIS,
105984 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
105985 * See the License for the specific language governing permissions and
105986 * limitations under the License.
105987 */
105988
105989 namespace JMS\Serializer\Tests\Fixtures;
105990
105991 use JMS\Serializer\Annotation\Type;
105992
105993 class CustomDeserializationObject
105994 {
105995 /**
105996 * @Type("string")
105997 */
105998 public $someProperty;
105999
106000 public function __construct($value)
106001 {
106002 $this->someProperty = $value;
106003 }
106004 }
106005 <?php
106006
106007 /*
106008 * Copyright 2013 Johannes M. Schmitt <schmittjoh@gmail.com>
106009 *
106010 * Licensed under the Apache License, Version 2.0 (the "License");
106011 * you may not use this file except in compliance with the License.
106012 * You may obtain a copy of the License at
106013 *
106014 * http://www.apache.org/licenses/LICENSE-2.0
106015 *
106016 * Unless required by applicable law or agreed to in writing, software
106017 * distributed under the License is distributed on an "AS IS" BASIS,
106018 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
106019 * See the License for the specific language governing permissions and
106020 * limitations under the License.
106021 */
106022
106023 namespace JMS\Serializer\Tests\Fixtures;
106024
106025 use JMS\Serializer\Annotation\Groups;
106026 use JMS\Serializer\Annotation\XmlValue;
106027 use JMS\Serializer\Annotation\XmlAttribute;
106028 use JMS\Serializer\Annotation\XmlList;
106029 use JMS\Serializer\Annotation\XmlMap;
106030 use JMS\Serializer\Annotation\Since;
106031 use JMS\Serializer\Annotation\Until;
106032 use JMS\Serializer\Annotation\VirtualProperty;
106033 use JMS\Serializer\Annotation\SerializedName;
106034
106035 /**
106036 * dummy comment
106037 */
106038 class ObjectWithVersionedVirtualProperties
106039 {
106040 /**
106041 * @Groups({"versions"})
106042 * @VirtualProperty
106043 * @SerializedName("low")
106044 * @Until("8")
106045 */
106046 public function getVirualLowValue()
106047 {
106048 return 1;
106049 }
106050
106051 /**
106052 * @Groups({"versions"})
106053 * @VirtualProperty
106054 * @SerializedName("high")
106055 * @Since("6")
106056 */
106057 public function getVirualHighValue()
106058 {
106059 return 8;
106060 }
106061 }<?php
106062
106063 /*
106064 * Copyright 2013 Johannes M. Schmitt <schmittjoh@gmail.com>
106065 *
106066 * Licensed under the Apache License, Version 2.0 (the "License");
106067 * you may not use this file except in compliance with the License.
106068 * You may obtain a copy of the License at
106069 *
106070 * http://www.apache.org/licenses/LICENSE-2.0
106071 *
106072 * Unless required by applicable law or agreed to in writing, software
106073 * distributed under the License is distributed on an "AS IS" BASIS,
106074 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
106075 * See the License for the specific language governing permissions and
106076 * limitations under the License.
106077 */
106078
106079 namespace JMS\Serializer\Tests\Fixtures;
106080
106081 use JMS\Serializer\Annotation\SerializedName;
106082 use JMS\Serializer\Annotation\Type;
106083 use JMS\Serializer\Annotation\XmlRoot;
106084 use JMS\Serializer\Annotation\ReadOnly;
106085 use JMS\Serializer\Annotation\Accessor;
106086
106087 /** @XmlRoot("author") */
106088 class AuthorReadOnly
106089 {
106090 /**
106091 * @ReadOnly
106092 * @SerializedName("id")
106093 */
106094 private $id;
106095
106096 /**
106097 * @Type("string")
106098 * @SerializedName("full_name")
106099 * @Accessor("getName")
106100 */
106101 private $name;
106102
106103 public function __construct($id, $name)
106104 {
106105 $this->id = $id;
106106 $this->name = $name;
106107 }
106108
106109 public function getId()
106110 {
106111 return $this->id;
106112 }
106113
106114 public function getName()
106115 {
106116 return $this->name;
106117 }
106118 }<?php
106119
106120 /*
106121 * Copyright 2013 Johannes M. Schmitt <schmittjoh@gmail.com>
106122 *
106123 * Licensed under the Apache License, Version 2.0 (the "License");
106124 * you may not use this file except in compliance with the License.
106125 * You may obtain a copy of the License at
106126 *
106127 * http://www.apache.org/licenses/LICENSE-2.0
106128 *
106129 * Unless required by applicable law or agreed to in writing, software
106130 * distributed under the License is distributed on an "AS IS" BASIS,
106131 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
106132 * See the License for the specific language governing permissions and
106133 * limitations under the License.
106134 */
106135
106136 namespace JMS\Serializer\Tests\Fixtures;
106137
106138 use JMS\Serializer\Annotation\Groups;
106139 use JMS\Serializer\Annotation\Type;
106140
106141 /** blablub */
106142 class GroupsObject
106143 {
106144 /**
106145 * @Groups({"foo"})
106146 * @Type("string")
106147 */
106148 private $foo;
106149
106150 /**
106151 * @Groups({"foo","bar"})
106152 * @Type("string")
106153 */
106154 private $foobar;
106155
106156 /**
106157 * @Groups({"bar", "Default"})
106158 * @Type("string")
106159 */
106160 private $bar;
106161
106162 /**
106163 * @Type("string")
106164 */
106165 private $none;
106166
106167 public function __construct()
106168 {
106169 $this->foo = "foo";
106170 $this->bar = "bar";
106171 $this->foobar = "foobar";
106172 $this->none = "none";
106173 }
106174 }<?php
106175
106176 /*
106177 * Copyright 2013 Johannes M. Schmitt <schmittjoh@gmail.com>
106178 *
106179 * Licensed under the Apache License, Version 2.0 (the "License");
106180 * you may not use this file except in compliance with the License.
106181 * You may obtain a copy of the License at
106182 *
106183 * http://www.apache.org/licenses/LICENSE-2.0
106184 *
106185 * Unless required by applicable law or agreed to in writing, software
106186 * distributed under the License is distributed on an "AS IS" BASIS,
106187 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
106188 * See the License for the specific language governing permissions and
106189 * limitations under the License.
106190 */
106191
106192 namespace JMS\Serializer\Tests\Fixtures;
106193
106194 use JMS\Serializer\Annotation\XmlAttribute;
106195 use JMS\Serializer\Annotation\XmlValue;
106196 use JMS\Serializer\Annotation\XmlRoot;
106197 use JMS\Serializer\Annotation\Type;
106198
106199 /**
106200 * @XmlRoot("child")
106201 */
106202 class Person
106203 {
106204 /**
106205 * @Type("string")
106206 * @XmlValue
106207 */
106208 public $name;
106209
106210 /**
106211 * @Type("integer")
106212 * @XmlAttribute
106213 */
106214 public $age;
106215 }
106216 <?php
106217
106218 /*
106219 * Copyright 2013 Johannes M. Schmitt <schmittjoh@gmail.com>
106220 *
106221 * Licensed under the Apache License, Version 2.0 (the "License");
106222 * you may not use this file except in compliance with the License.
106223 * You may obtain a copy of the License at
106224 *
106225 * http://www.apache.org/licenses/LICENSE-2.0
106226 *
106227 * Unless required by applicable law or agreed to in writing, software
106228 * distributed under the License is distributed on an "AS IS" BASIS,
106229 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
106230 * See the License for the specific language governing permissions and
106231 * limitations under the License.
106232 */
106233
106234 namespace JMS\Serializer\Tests\Fixtures;
106235
106236 use JMS\Serializer\Annotation as Serializer;
106237
106238 /**
106239 * An array-acting object that holds many author instances.
106240 */
106241 class AuthorList implements \IteratorAggregate, \Countable, \ArrayAccess
106242 {
106243 /**
106244 * @Serializer\Type("array<JMS\Serializer\Tests\Fixtures\Author>")
106245 * @var array
106246 */
106247 protected $authors = array();
106248
106249 /**
106250 * @param Author $author
106251 */
106252 public function add(Author $author)
106253 {
106254 $this->authors[] = $author;
106255 }
106256
106257 /**
106258 * @see IteratorAggregate
106259 */
106260 public function getIterator()
106261 {
106262 return new \ArrayIterator($this->authors);
106263 }
106264
106265 /**
106266 * @see Countable
106267 */
106268 public function count()
106269 {
106270 return count($this->authors);
106271 }
106272
106273 /**
106274 * @see ArrayAccess
106275 */
106276 public function offsetExists($offset)
106277 {
106278 return isset($this->authors[$offset]);
106279 }
106280
106281 /**
106282 * @see ArrayAccess
106283 */
106284 public function offsetGet($offset)
106285 {
106286 return isset($this->authors[$offset]) ? $this->authors[$offset] : null;
106287 }
106288
106289 /**
106290 * @see ArrayAccess
106291 */
106292 public function offsetSet($offset, $value)
106293 {
106294 if (null === $offset) {
106295 $this->authors[] = $value;
106296 } else {
106297 $this->authors[$offset] = $value;
106298 }
106299 }
106300
106301 /**
106302 * @see ArrayAccess
106303 */
106304 public function offsetUnset($offset)
106305 {
106306 unset($this->authors[$offset]);
106307 }
106308
106309 }
106310 <?php
106311
106312 /*
106313 * Copyright 2013 Johannes M. Schmitt <schmittjoh@gmail.com>
106314 *
106315 * Licensed under the Apache License, Version 2.0 (the "License");
106316 * you may not use this file except in compliance with the License.
106317 * You may obtain a copy of the License at
106318 *
106319 * http://www.apache.org/licenses/LICENSE-2.0
106320 *
106321 * Unless required by applicable law or agreed to in writing, software
106322 * distributed under the License is distributed on an "AS IS" BASIS,
106323 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
106324 * See the License for the specific language governing permissions and
106325 * limitations under the License.
106326 */
106327
106328 namespace JMS\Serializer\Tests\Fixtures;
106329
106330 use JMS\Serializer\Annotation\Accessor;
106331 use JMS\Serializer\Annotation\XmlMap;
106332 use JMS\Serializer\Annotation\XmlRoot;
106333 use JMS\Serializer\Annotation\XmlList;
106334 use JMS\Serializer\Annotation\XmlAttribute;
106335
106336 /** @XmlRoot("post") */
106337 class IndexedCommentsBlogPost
106338 {
106339 /**
106340 * @XmlMap(keyAttribute="author-name", inline=true, entry="comments")
106341 * @Accessor(getter="getCommentsIndexedByAuthor")
106342 */
106343 private $comments = array();
106344
106345 public function __construct()
106346 {
106347 $author = new Author('Foo');
106348 $this->comments[] = new Comment($author, 'foo');
106349 $this->comments[] = new Comment($author, 'bar');
106350 }
106351
106352 public function getCommentsIndexedByAuthor()
106353 {
106354 $indexedComments = array();
106355 foreach ($this->comments as $comment) {
106356 $authorName = $comment->getAuthor()->getName();
106357
106358 if (!isset($indexedComments[$authorName])) {
106359 $indexedComments[$authorName] = new IndexedCommentsList();
106360 }
106361
106362 $indexedComments[$authorName]->addComment($comment);
106363 }
106364
106365 return $indexedComments;
106366 }
106367 }
106368
106369 class IndexedCommentsList
106370 {
106371 /** @XmlList(inline=true, entry="comment") */
106372 private $comments = array();
106373
106374 /** @XmlAttribute */
106375 private $count = 0;
106376
106377 public function addComment(Comment $comment)
106378 {
106379 $this->comments[] = $comment;
106380 $this->count += 1;
106381 }
106382 }<?php
106383
106384 /*
106385 * Copyright 2013 Johannes M. Schmitt <schmittjoh@gmail.com>
106386 *
106387 * Licensed under the Apache License, Version 2.0 (the "License");
106388 * you may not use this file except in compliance with the License.
106389 * You may obtain a copy of the License at
106390 *
106391 * http://www.apache.org/licenses/LICENSE-2.0
106392 *
106393 * Unless required by applicable law or agreed to in writing, software
106394 * distributed under the License is distributed on an "AS IS" BASIS,
106395 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
106396 * See the License for the specific language governing permissions and
106397 * limitations under the License.
106398 */
106399
106400 namespace JMS\Serializer\Tests\Fixtures;
106401
106402 use JMS\Serializer\Annotation as Serializer;
106403
106404 /** @Serializer\AccessorOrder("custom", custom = {"method", "b", "a"}) */
106405 class AccessorOrderMethod
106406 {
106407 private $b = 'b', $a = 'a';
106408
106409 /**
106410 * @Serializer\VirtualProperty
106411 * @Serializer\SerializedName("foo")
106412 *
106413 * @return string
106414 */
106415 public function getMethod()
106416 {
106417 return 'c';
106418 }
106419 }<?php
106420
106421 /*
106422 * Copyright 2013 Johannes M. Schmitt <schmittjoh@gmail.com>
106423 *
106424 * Licensed under the Apache License, Version 2.0 (the "License");
106425 * you may not use this file except in compliance with the License.
106426 * You may obtain a copy of the License at
106427 *
106428 * http://www.apache.org/licenses/LICENSE-2.0
106429 *
106430 * Unless required by applicable law or agreed to in writing, software
106431 * distributed under the License is distributed on an "AS IS" BASIS,
106432 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
106433 * See the License for the specific language governing permissions and
106434 * limitations under the License.
106435 */
106436
106437 namespace JMS\Serializer\Tests\Fixtures;
106438
106439 use JMS\Serializer\Annotation\Groups;
106440 use JMS\Serializer\Annotation\XmlValue;
106441 use JMS\Serializer\Annotation\XmlAttribute;
106442 use JMS\Serializer\Annotation\XmlList;
106443 use JMS\Serializer\Annotation\XmlMap;
106444 use JMS\Serializer\Annotation\Since;
106445 use JMS\Serializer\Annotation\Until;
106446 use JMS\Serializer\Annotation\VirtualProperty;
106447 use JMS\Serializer\Annotation\SerializedName;
106448
106449
106450 class ObjectWithVirtualXmlProperties
106451 {
106452
106453 /**
106454 *
106455 * @VirtualProperty
106456 * @SerializedName("foo")
106457 * @Groups({"attributes"})
106458 * @XmlAttribute
106459 */
106460 public function getVirualXmlAttributeValue()
106461 {
106462 return 'bar';
106463 }
106464
106465 /**
106466 *
106467 * @VirtualProperty
106468 * @SerializedName("xml-value")
106469 * @Groups({"values"})
106470 * @XmlValue
106471 */
106472 public function getVirualXmlValue()
106473 {
106474 return 'xml-value';
106475 }
106476
106477 /**
106478 *
106479 * @VirtualProperty
106480 * @SerializedName("list")
106481 * @Groups({"list"})
106482 * @XmlList(inline = true, entry = "val")
106483 */
106484 public function getVirualXmlList()
106485 {
106486 return array('One','Two');
106487 }
106488
106489 /**
106490 *
106491 * @VirtualProperty
106492 * @SerializedName("map")
106493 * @Groups({"map"})
106494 * @XmlMap(keyAttribute = "key")
106495 */
106496 public function getVirualXmlMap()
106497 {
106498 return array(
106499 'key-one' => 'One',
106500 'key-two' => 'Two'
106501 );
106502 }
106503
106504 /**
106505 *
106506 * @VirtualProperty
106507 * @SerializedName("low")
106508 * @Groups({"versions"})
106509 * @Until("8")
106510 */
106511 public function getVirualLowValue()
106512 {
106513 return 1;
106514 }
106515
106516 /**
106517 * @VirtualProperty
106518 * @SerializedName("hight")
106519 * @Groups({"versions"})
106520 * @Since("8")
106521 */
106522 public function getVirualHighValue()
106523 {
106524 return 8;
106525 }
106526
106527 }<?php
106528
106529 /*
106530 * Copyright 2013 Johannes M. Schmitt <schmittjoh@gmail.com>
106531 *
106532 * Licensed under the Apache License, Version 2.0 (the "License");
106533 * you may not use this file except in compliance with the License.
106534 * You may obtain a copy of the License at
106535 *
106536 * http://www.apache.org/licenses/LICENSE-2.0
106537 *
106538 * Unless required by applicable law or agreed to in writing, software
106539 * distributed under the License is distributed on an "AS IS" BASIS,
106540 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
106541 * See the License for the specific language governing permissions and
106542 * limitations under the License.
106543 */
106544
106545 namespace JMS\Serializer\Tests\Fixtures;
106546
106547 use JMS\Serializer\Annotation\Groups;
106548 use JMS\Serializer\Annotation\Type;
106549
106550 class InvalidGroupsObject
106551 {
106552 /**
106553 * @Groups({"foo, bar"})
106554 * @Type("string")
106555 */
106556 private $foo;
106557 }<?php
106558
106559 /*
106560 * Copyright 2013 Johannes M. Schmitt <schmittjoh@gmail.com>
106561 *
106562 * Licensed under the Apache License, Version 2.0 (the "License");
106563 * you may not use this file except in compliance with the License.
106564 * You may obtain a copy of the License at
106565 *
106566 * http://www.apache.org/licenses/LICENSE-2.0
106567 *
106568 * Unless required by applicable law or agreed to in writing, software
106569 * distributed under the License is distributed on an "AS IS" BASIS,
106570 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
106571 * See the License for the specific language governing permissions and
106572 * limitations under the License.
106573 */
106574
106575 namespace JMS\Serializer\Tests\Fixtures;
106576
106577 use JMS\Serializer\Annotation\SerializedName;
106578 use JMS\Serializer\Annotation\Type;
106579
106580 class SimpleObject
106581 {
106582 /** @Type("string") */
106583 private $foo;
106584
106585 /**
106586 * @SerializedName("moo")
106587 * @Type("string")
106588 */
106589 private $bar;
106590
106591 /** @Type("string") */
106592 protected $camelCase = 'boo';
106593
106594 public function __construct($foo, $bar)
106595 {
106596 $this->foo = $foo;
106597 $this->bar = $bar;
106598 }
106599
106600 public function getFoo()
106601 {
106602 return $this->foo;
106603 }
106604
106605 public function getBar()
106606 {
106607 return $this->bar;
106608 }
106609 }<?php
106610
106611 /*
106612 * Copyright 2013 Johannes M. Schmitt <schmittjoh@gmail.com>
106613 *
106614 * Licensed under the Apache License, Version 2.0 (the "License");
106615 * you may not use this file except in compliance with the License.
106616 * You may obtain a copy of the License at
106617 *
106618 * http://www.apache.org/licenses/LICENSE-2.0
106619 *
106620 * Unless required by applicable law or agreed to in writing, software
106621 * distributed under the License is distributed on an "AS IS" BASIS,
106622 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
106623 * See the License for the specific language governing permissions and
106624 * limitations under the License.
106625 */
106626
106627 namespace JMS\Serializer\Tests\Fixtures;
106628
106629 use JMS\Serializer\Annotation\XmlRoot;
106630 use JMS\Serializer\Annotation\Type;
106631
106632 /** @XmlRoot("order") */
106633 class Order
106634 {
106635 /** @Type("JMS\Serializer\Tests\Fixtures\Price") */
106636 private $cost;
106637
106638 public function __construct(Price $price = null)
106639 {
106640 $this->cost = $price ?: new Price(5);
106641 }
106642 }<?php
106643
106644 /*
106645 * Copyright 2013 Johannes M. Schmitt <schmittjoh@gmail.com>
106646 *
106647 * Licensed under the Apache License, Version 2.0 (the "License");
106648 * you may not use this file except in compliance with the License.
106649 * You may obtain a copy of the License at
106650 *
106651 * http://www.apache.org/licenses/LICENSE-2.0
106652 *
106653 * Unless required by applicable law or agreed to in writing, software
106654 * distributed under the License is distributed on an "AS IS" BASIS,
106655 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
106656 * See the License for the specific language governing permissions and
106657 * limitations under the License.
106658 */
106659
106660 namespace JMS\Serializer\Tests\Fixtures;
106661
106662 use JMS\Serializer\Annotation\XmlKeyValuePairs;
106663
106664 class ObjectWithXmlKeyValuePairs
106665 {
106666 /**
106667 * @var array
106668 * @XmlKeyValuePairs
106669 */
106670 private $array = array(
106671 'key-one' => 'foo',
106672 'key-two' => 1,
106673 'nested-array' => array(
106674 'bar' => 'foo',
106675 ),
106676 'without-keys' => array(
106677 1,
106678 'test'
106679 ),
106680 'mixed' => array(
106681 'test',
106682 'foo' => 'bar',
106683 '1_foo' => 'bar'
106684 ),
106685 1 => 'foo'
106686 );
106687 }
106688 <?php
106689
106690 /*
106691 * Copyright 2013 Johannes M. Schmitt <schmittjoh@gmail.com>
106692 *
106693 * Licensed under the Apache License, Version 2.0 (the "License");
106694 * you may not use this file except in compliance with the License.
106695 * You may obtain a copy of the License at
106696 *
106697 * http://www.apache.org/licenses/LICENSE-2.0
106698 *
106699 * Unless required by applicable law or agreed to in writing, software
106700 * distributed under the License is distributed on an "AS IS" BASIS,
106701 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
106702 * See the License for the specific language governing permissions and
106703 * limitations under the License.
106704 */
106705
106706 namespace JMS\Serializer\Tests\Fixtures;
106707
106708 use JMS\Serializer\Annotation as Serializer;
106709
106710 class Node
106711 {
106712 /**
106713 * @Serializer\MaxDepth(2)
106714 */
106715 public $children;
106716
106717 public $foo = 'bar';
106718
106719 public function __construct($children = array())
106720 {
106721 $this->children = $children;
106722 }
106723 }
106724 <?php
106725
106726 /*
106727 * Copyright 2013 Johannes M. Schmitt <schmittjoh@gmail.com>
106728 *
106729 * Licensed under the Apache License, Version 2.0 (the "License");
106730 * you may not use this file except in compliance with the License.
106731 * You may obtain a copy of the License at
106732 *
106733 * http://www.apache.org/licenses/LICENSE-2.0
106734 *
106735 * Unless required by applicable law or agreed to in writing, software
106736 * distributed under the License is distributed on an "AS IS" BASIS,
106737 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
106738 * See the License for the specific language governing permissions and
106739 * limitations under the License.
106740 */
106741
106742 namespace JMS\Serializer\Tests\Fixtures;
106743
106744 use JMS\Serializer\Tests\Fixtures\SimpleObject;
106745
106746 class ObjectWithNullProperty extends SimpleObject
106747 {
106748 private $nullProperty = null;
106749 }
106750 <?php
106751
106752 /*
106753 * Copyright 2013 Johannes M. Schmitt <schmittjoh@gmail.com>
106754 *
106755 * Licensed under the Apache License, Version 2.0 (the "License");
106756 * you may not use this file except in compliance with the License.
106757 * You may obtain a copy of the License at
106758 *
106759 * http://www.apache.org/licenses/LICENSE-2.0
106760 *
106761 * Unless required by applicable law or agreed to in writing, software
106762 * distributed under the License is distributed on an "AS IS" BASIS,
106763 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
106764 * See the License for the specific language governing permissions and
106765 * limitations under the License.
106766 */
106767
106768 namespace JMS\Serializer\Tests\Fixtures;
106769
106770 use JMS\Serializer\Context;
106771 use JMS\Serializer\JsonDeserializationVisitor;
106772 use JMS\Serializer\XmlDeserializationVisitor;
106773 use Symfony\Component\Yaml\Inline;
106774 use JMS\Serializer\YamlSerializationVisitor;
106775 use JMS\Serializer\JsonSerializationVisitor;
106776 use JMS\Serializer\XmlSerializationVisitor;
106777 use JMS\Serializer\Annotation\HandlerCallback;
106778
106779 class Article
106780 {
106781 public $element;
106782 public $value;
106783
106784 /** @HandlerCallback("xml", direction = "serialization") */
106785 public function serializeToXml(XmlSerializationVisitor $visitor, $data, Context $context)
106786 {
106787 if (null === $visitor->document) {
106788 $visitor->document = $visitor->createDocument(null, null, false);
106789 }
106790
106791 $visitor->document->appendChild($visitor->document->createElement($this->element, $this->value));
106792 }
106793
106794 /** @HandlerCallback("json", direction = "serialization") */
106795 public function serializeToJson(JsonSerializationVisitor $visitor)
106796 {
106797 $visitor->setRoot(array($this->element => $this->value));
106798 }
106799
106800 /** @HandlerCallback("yml", direction = "serialization") */
106801 public function serializeToYml(YamlSerializationVisitor $visitor)
106802 {
106803 $visitor->writer->writeln(Inline::dump($this->element).': '.Inline::dump($this->value));
106804 }
106805
106806 /** @HandlerCallback("xml", direction = "deserialization") */
106807 public function deserializeFromXml(XmlDeserializationVisitor $visitor, \SimpleXMLElement $data)
106808 {
106809 $this->element = $data->getName();
106810 $this->value = (string) $data;
106811 }
106812
106813 /** @HandlerCallback("json", direction = "deserialization") */
106814 public function deserializeFromJson(JsonDeserializationVisitor $visitor, array $data)
106815 {
106816 $this->element = key($data);
106817 $this->value = reset($data);
106818 }
106819 }<?php
106820
106821 /*
106822 * Copyright 2013 Johannes M. Schmitt <schmittjoh@gmail.com>
106823 *
106824 * Licensed under the Apache License, Version 2.0 (the "License");
106825 * you may not use this file except in compliance with the License.
106826 * You may obtain a copy of the License at
106827 *
106828 * http://www.apache.org/licenses/LICENSE-2.0
106829 *
106830 * Unless required by applicable law or agreed to in writing, software
106831 * distributed under the License is distributed on an "AS IS" BASIS,
106832 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
106833 * See the License for the specific language governing permissions and
106834 * limitations under the License.
106835 */
106836
106837 namespace JMS\Serializer\Tests\Fixtures;
106838
106839 use JMS\Serializer\Annotation\Type;
106840 use JMS\Serializer\Annotation\SerializedName;
106841 use JMS\Serializer\Annotation\XmlMap;
106842 use JMS\Serializer\Annotation\XmlRoot;
106843 use JMS\Serializer\Annotation\XmlAttribute;
106844 use JMS\Serializer\Annotation\XmlList;
106845 use JMS\Serializer\Annotation\Groups;
106846 use Doctrine\Common\Collections\ArrayCollection;
106847 use PhpCollection\Map;
106848 use PhpCollection\Sequence;
106849
106850 /** @XmlRoot("blog-post") */
106851 class BlogPost
106852 {
106853 /**
106854 * @Type("string")
106855 * @Groups({"comments","post"})
106856 */
106857 private $title;
106858
106859 /**
106860 * @Type("DateTime")
106861 * @XmlAttribute
106862 */
106863 private $createdAt;
106864
106865 /**
106866 * @Type("boolean")
106867 * @SerializedName("is_published")
106868 * @XmlAttribute
106869 * @Groups({"post"})
106870 */
106871 private $published;
106872
106873 /**
106874 * @Type("ArrayCollection<JMS\Serializer\Tests\Fixtures\Comment>")
106875 * @XmlList(inline=true, entry="comment")
106876 * @Groups({"comments"})
106877 */
106878 private $comments;
106879
106880 /**
106881 * @Type("PhpCollection\Sequence<JMS\Serializer\Tests\Fixtures\Comment>")
106882 * @XmlList(inline=true, entry="comment2")
106883 * @Groups({"comments"})
106884 */
106885 private $comments2;
106886
106887 /**
106888 * @Type("PhpCollection\Map<string,string>")
106889 * @XmlMap(keyAttribute = "key")
106890 */
106891 private $metadata;
106892
106893 /**
106894 * @Type("JMS\Serializer\Tests\Fixtures\Author")
106895 * @Groups({"post"})
106896 */
106897 private $author;
106898
106899 public function __construct($title, Author $author, \DateTime $createdAt)
106900 {
106901 $this->title = $title;
106902 $this->author = $author;
106903 $this->published = false;
106904 $this->comments = new ArrayCollection();
106905 $this->comments2 = new Sequence();
106906 $this->metadata = new Map();
106907 $this->metadata->set('foo', 'bar');
106908 $this->createdAt = $createdAt;
106909 }
106910
106911 public function setPublished()
106912 {
106913 $this->published = true;
106914 }
106915
106916 public function getMetadata()
106917 {
106918 return $this->metadata;
106919 }
106920
106921 public function addComment(Comment $comment)
106922 {
106923 $this->comments->add($comment);
106924 $this->comments2->add($comment);
106925 }
106926 }<?php
106927
106928 /*
106929 * Copyright 2013 Johannes M. Schmitt <schmittjoh@gmail.com>
106930 *
106931 * Licensed under the Apache License, Version 2.0 (the "License");
106932 * you may not use this file except in compliance with the License.
106933 * You may obtain a copy of the License at
106934 *
106935 * http://www.apache.org/licenses/LICENSE-2.0
106936 *
106937 * Unless required by applicable law or agreed to in writing, software
106938 * distributed under the License is distributed on an "AS IS" BASIS,
106939 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
106940 * See the License for the specific language governing permissions and
106941 * limitations under the License.
106942 */
106943
106944 namespace JMS\Serializer\Tests\Fixtures;
106945
106946 use JMS\Serializer\Annotation\Type;
106947
106948 class Comment
106949 {
106950 /**
106951 * @Type("JMS\Serializer\Tests\Fixtures\Author")
106952 */
106953 private $author;
106954
106955 /**
106956 * @Type("string")
106957 */
106958 private $text;
106959
106960 public function __construct(Author $author = null, $text)
106961 {
106962 $this->author = $author;
106963 $this->text = $text;
106964 }
106965
106966 public function getAuthor()
106967 {
106968 return $this->author;
106969 }
106970 }<?php
106971
106972 /*
106973 * Copyright 2013 Johannes M. Schmitt <schmittjoh@gmail.com>
106974 *
106975 * Licensed under the Apache License, Version 2.0 (the "License");
106976 * you may not use this file except in compliance with the License.
106977 * You may obtain a copy of the License at
106978 *
106979 * http://www.apache.org/licenses/LICENSE-2.0
106980 *
106981 * Unless required by applicable law or agreed to in writing, software
106982 * distributed under the License is distributed on an "AS IS" BASIS,
106983 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
106984 * See the License for the specific language governing permissions and
106985 * limitations under the License.
106986 */
106987
106988 namespace JMS\Serializer\Tests\Fixtures;
106989
106990 use JMS\Serializer\Annotation\Type;
106991
106992 class CircularReferenceChild
106993 {
106994 /** @Type("string") */
106995 private $name;
106996
106997 /** @Type("JMS\Serializer\Tests\Fixtures\CircularReferenceParent") */
106998 private $parent;
106999
107000 public function __construct($name, CircularReferenceParent $parent)
107001 {
107002 $this->name = $name;
107003 $this->parent = $parent;
107004 }
107005
107006 public function getName()
107007 {
107008 return $this->name;
107009 }
107010
107011 public function getParent()
107012 {
107013 return $this->parent;
107014 }
107015
107016 public function setParent(CircularReferenceParent $parent)
107017 {
107018 $this->parent = $parent;
107019 }
107020 }<?php
107021
107022 /*
107023 * Copyright 2013 Johannes M. Schmitt <schmittjoh@gmail.com>
107024 *
107025 * Licensed under the Apache License, Version 2.0 (the "License");
107026 * you may not use this file except in compliance with the License.
107027 * You may obtain a copy of the License at
107028 *
107029 * http://www.apache.org/licenses/LICENSE-2.0
107030 *
107031 * Unless required by applicable law or agreed to in writing, software
107032 * distributed under the License is distributed on an "AS IS" BASIS,
107033 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
107034 * See the License for the specific language governing permissions and
107035 * limitations under the License.
107036 */
107037
107038 namespace JMS\Serializer\Tests\Fixtures;
107039
107040 use JMS\Serializer\Annotation as Serializer;
107041
107042 /** @Serializer\AccessorOrder("alphabetical") */
107043 class AccessorOrderParent
107044 {
107045 private $b = 'b', $a = 'a';
107046 }<?php
107047
107048 /*
107049 * Copyright 2013 Johannes M. Schmitt <schmittjoh@gmail.com>
107050 *
107051 * Licensed under the Apache License, Version 2.0 (the "License");
107052 * you may not use this file except in compliance with the License.
107053 * You may obtain a copy of the License at
107054 *
107055 * http://www.apache.org/licenses/LICENSE-2.0
107056 *
107057 * Unless required by applicable law or agreed to in writing, software
107058 * distributed under the License is distributed on an "AS IS" BASIS,
107059 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
107060 * See the License for the specific language governing permissions and
107061 * limitations under the License.
107062 */
107063
107064 namespace JMS\Serializer\Tests\Fixtures;
107065
107066 use Doctrine\ORM\Proxy\Proxy;
107067
107068 class SimpleObjectProxy extends SimpleObject implements Proxy
107069 {
107070 public $__isInitialized__ = false;
107071
107072 private $baz = 'baz';
107073
107074 public function __load()
107075 {
107076 if (!$this->__isInitialized__) {
107077 $this->camelCase = 'proxy-boo';
107078 $this->__isInitialized__ = true;
107079 }
107080 }
107081
107082 public function __isInitialized()
107083 {
107084 return $this->__isInitialized__;
107085 }
107086 }<?php
107087
107088 /*
107089 * Copyright 2013 Johannes M. Schmitt <schmittjoh@gmail.com>
107090 *
107091 * Licensed under the Apache License, Version 2.0 (the "License");
107092 * you may not use this file except in compliance with the License.
107093 * You may obtain a copy of the License at
107094 *
107095 * http://www.apache.org/licenses/LICENSE-2.0
107096 *
107097 * Unless required by applicable law or agreed to in writing, software
107098 * distributed under the License is distributed on an "AS IS" BASIS,
107099 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
107100 * See the License for the specific language governing permissions and
107101 * limitations under the License.
107102 */
107103
107104 namespace JMS\Serializer\Tests\Fixtures;
107105
107106 use JMS\Serializer\Annotation\AccessType;
107107 use JMS\Serializer\Annotation\Type;
107108 use JMS\Serializer\Annotation\ReadOnly;
107109
107110 /** @AccessType("public_method") */
107111 class GetSetObject
107112 {
107113 /** @AccessType("property") @Type("integer") */
107114 private $id = 1;
107115
107116 /** @Type("string") */
107117 private $name = 'Foo';
107118
107119 /**
107120 * @ReadOnly
107121 */
107122 private $readOnlyProperty = 42;
107123
107124 public function getId()
107125 {
107126 throw new \RuntimeException('This should not be called.');
107127 }
107128
107129 public function getName()
107130 {
107131 return 'Johannes';
107132 }
107133
107134 public function setName($name)
107135 {
107136 $this->name = $name;
107137 }
107138
107139 public function getReadOnlyProperty()
107140 {
107141 return $this->readOnlyProperty;
107142 }
107143 }
107144 <?php
107145
107146 /*
107147 * Copyright 2013 Johannes M. Schmitt <schmittjoh@gmail.com>
107148 *
107149 * Licensed under the Apache License, Version 2.0 (the "License");
107150 * you may not use this file except in compliance with the License.
107151 * You may obtain a copy of the License at
107152 *
107153 * http://www.apache.org/licenses/LICENSE-2.0
107154 *
107155 * Unless required by applicable law or agreed to in writing, software
107156 * distributed under the License is distributed on an "AS IS" BASIS,
107157 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
107158 * See the License for the specific language governing permissions and
107159 * limitations under the License.
107160 */
107161
107162 namespace JMS\Serializer\Tests\Fixtures;
107163
107164 use JMS\Serializer\Annotation as Serializer;
107165 use JMS\Serializer\Annotation\Type;
107166
107167 /** @Serializer\AccessorOrder("alphabetical") */
107168 class InlineParent
107169 {
107170 /**
107171 * @Type("string")
107172 */
107173 private $c = 'c';
107174
107175 /**
107176 * @Type("string")
107177 */
107178 private $d = 'd';
107179
107180 /**
107181 * @Type("JMS\Serializer\Tests\Fixtures\InlineChild")
107182 * @Serializer\Inline
107183 */
107184 private $child;
107185
107186 public function __construct()
107187 {
107188 $this->child = new InlineChild();
107189 }
107190 }<?php
107191
107192 /*
107193 * Copyright 2013 Johannes M. Schmitt <schmittjoh@gmail.com>
107194 *
107195 * Licensed under the Apache License, Version 2.0 (the "License");
107196 * you may not use this file except in compliance with the License.
107197 * You may obtain a copy of the License at
107198 *
107199 * http://www.apache.org/licenses/LICENSE-2.0
107200 *
107201 * Unless required by applicable law or agreed to in writing, software
107202 * distributed under the License is distributed on an "AS IS" BASIS,
107203 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
107204 * See the License for the specific language governing permissions and
107205 * limitations under the License.
107206 */
107207
107208 namespace JMS\Serializer\Tests\Fixtures;
107209
107210 use JMS\Serializer\Annotation as Serializer;
107211
107212 class Tree
107213 {
107214 /**
107215 * @Serializer\MaxDepth(10)
107216 */
107217 public $tree;
107218
107219 public function __construct($tree)
107220 {
107221 $this->tree = $tree;
107222 }
107223 }
107224 <?php
107225
107226 /*
107227 * Copyright 2013 Johannes M. Schmitt <schmittjoh@gmail.com>
107228 *
107229 * Licensed under the Apache License, Version 2.0 (the "License");
107230 * you may not use this file except in compliance with the License.
107231 * You may obtain a copy of the License at
107232 *
107233 * http://www.apache.org/licenses/LICENSE-2.0
107234 *
107235 * Unless required by applicable law or agreed to in writing, software
107236 * distributed under the License is distributed on an "AS IS" BASIS,
107237 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
107238 * See the License for the specific language governing permissions and
107239 * limitations under the License.
107240 */
107241
107242 namespace JMS\Serializer\Tests\Fixtures;
107243
107244 use JMS\Serializer\Annotation\SerializedName;
107245 use JMS\Serializer\Annotation\Type;
107246
107247 class Author
107248 {
107249 /**
107250 * @Type("string")
107251 * @SerializedName("full_name")
107252 */
107253 private $name;
107254
107255 public function __construct($name)
107256 {
107257 $this->name = $name;
107258 }
107259
107260 public function getName()
107261 {
107262 return $this->name;
107263 }
107264 }<?php
107265
107266 /*
107267 * Copyright 2013 Johannes M. Schmitt <schmittjoh@gmail.com>
107268 *
107269 * Licensed under the Apache License, Version 2.0 (the "License");
107270 * you may not use this file except in compliance with the License.
107271 * You may obtain a copy of the License at
107272 *
107273 * http://www.apache.org/licenses/LICENSE-2.0
107274 *
107275 * Unless required by applicable law or agreed to in writing, software
107276 * distributed under the License is distributed on an "AS IS" BASIS,
107277 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
107278 * See the License for the specific language governing permissions and
107279 * limitations under the License.
107280 */
107281
107282 namespace JMS\Serializer\Tests\Fixtures;
107283
107284 use JMS\Serializer\Annotation\Type;
107285 use JMS\Serializer\Annotation\XmlValue;
107286 use JMS\Serializer\Annotation\XmlRoot;
107287
107288 /**
107289 * @XmlRoot("price")
107290 */
107291 class Price
107292 {
107293 /**
107294 * @Type("double")
107295 * @XmlValue
107296 */
107297 private $price;
107298
107299 function __construct($price)
107300 {
107301 $this->price = $price;
107302 }
107303 }<?php
107304
107305 /*
107306 * Copyright 2013 Johannes M. Schmitt <schmittjoh@gmail.com>
107307 *
107308 * Licensed under the Apache License, Version 2.0 (the "License");
107309 * you may not use this file except in compliance with the License.
107310 * You may obtain a copy of the License at
107311 *
107312 * http://www.apache.org/licenses/LICENSE-2.0
107313 *
107314 * Unless required by applicable law or agreed to in writing, software
107315 * distributed under the License is distributed on an "AS IS" BASIS,
107316 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
107317 * See the License for the specific language governing permissions and
107318 * limitations under the License.
107319 */
107320
107321 namespace JMS\Serializer\Tests\Fixtures;
107322
107323 use JMS\Serializer\Annotation\XmlValue;
107324
107325 /** Dummy */
107326 class InvalidUsageOfXmlValue
107327 {
107328 /** @XmlValue */
107329 private $value = 'bar';
107330
107331 private $element = 'foo';
107332 }<?php
107333
107334 /*
107335 * Copyright 2013 Johannes M. Schmitt <schmittjoh@gmail.com>
107336 *
107337 * Licensed under the Apache License, Version 2.0 (the "License");
107338 * you may not use this file except in compliance with the License.
107339 * You may obtain a copy of the License at
107340 *
107341 * http://www.apache.org/licenses/LICENSE-2.0
107342 *
107343 * Unless required by applicable law or agreed to in writing, software
107344 * distributed under the License is distributed on an "AS IS" BASIS,
107345 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
107346 * See the License for the specific language governing permissions and
107347 * limitations under the License.
107348 */
107349
107350 namespace JMS\Serializer\Tests\Fixtures;
107351
107352 use JMS\Serializer\Annotation\AccessorOrder;
107353 use JMS\Serializer\Annotation\Type;
107354 use JMS\Serializer\Annotation\VirtualProperty;
107355 use JMS\Serializer\Annotation\SerializedName;
107356 use JMS\Serializer\Annotation\Exclude;
107357
107358 /**
107359 * @AccessorOrder("custom", custom = {"prop_name", "existField", "foo" })
107360 */
107361 class ObjectWithVirtualProperties
107362 {
107363
107364 /**
107365 * @Type("string")
107366 */
107367 protected $existField = 'value';
107368
107369 /**
107370 *
107371 * @VirtualProperty
107372 */
107373 public function getVirtualValue()
107374 {
107375 return 'value';
107376 }
107377
107378 /**
107379 * @VirtualProperty
107380 * @SerializedName("test")
107381 */
107382 public function getVirtualSerializedValue()
107383 {
107384 return 'other-name';
107385 }
107386
107387 /**
107388 * @VirtualProperty
107389 * @Type("integer")
107390 */
107391 public function getTypedVirtualProperty()
107392 {
107393 return '1';
107394 }
107395 }
107396 <?php
107397
107398 /*
107399 * Copyright 2013 Johannes M. Schmitt <schmittjoh@gmail.com>
107400 *
107401 * Licensed under the Apache License, Version 2.0 (the "License");
107402 * you may not use this file except in compliance with the License.
107403 * You may obtain a copy of the License at
107404 *
107405 * http://www.apache.org/licenses/LICENSE-2.0
107406 *
107407 * Unless required by applicable law or agreed to in writing, software
107408 * distributed under the License is distributed on an "AS IS" BASIS,
107409 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
107410 * See the License for the specific language governing permissions and
107411 * limitations under the License.
107412 */
107413
107414 namespace JMS\Serializer\Tests\Fixtures;
107415
107416 use JMS\Serializer\Annotation\Exclude;
107417 use JMS\Serializer\Annotation\PreSerialize;
107418 use JMS\Serializer\Annotation\PostSerialize;
107419 use JMS\Serializer\Annotation\PostDeserialize;
107420 use JMS\Serializer\Annotation\Type;
107421
107422 class ObjectWithLifecycleCallbacks
107423 {
107424 /**
107425 * @Exclude
107426 */
107427 private $firstname;
107428
107429 /**
107430 * @Exclude
107431 */
107432 private $lastname;
107433
107434 /**
107435 * @Type("string")
107436 */
107437 private $name;
107438
107439 public function __construct($firstname = 'Foo', $lastname = 'Bar')
107440 {
107441 $this->firstname = $firstname;
107442 $this->lastname = $lastname;
107443 }
107444
107445 /**
107446 * @PreSerialize
107447 */
107448 private function prepareForSerialization()
107449 {
107450 $this->name = $this->firstname.' '.$this->lastname;
107451 }
107452
107453 /**
107454 * @PostSerialize
107455 */
107456 private function cleanUpAfterSerialization()
107457 {
107458 $this->name = null;
107459 }
107460
107461 /**
107462 * @PostDeserialize
107463 */
107464 private function afterDeserialization()
107465 {
107466 list($this->firstname, $this->lastname) = explode(' ', $this->name);
107467 $this->name = null;
107468 }
107469 }<?php
107470
107471 /*
107472 * Copyright 2013 Johannes M. Schmitt <schmittjoh@gmail.com>
107473 *
107474 * Licensed under the Apache License, Version 2.0 (the "License");
107475 * you may not use this file except in compliance with the License.
107476 * You may obtain a copy of the License at
107477 *
107478 * http://www.apache.org/licenses/LICENSE-2.0
107479 *
107480 * Unless required by applicable law or agreed to in writing, software
107481 * distributed under the License is distributed on an "AS IS" BASIS,
107482 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
107483 * See the License for the specific language governing permissions and
107484 * limitations under the License.
107485 */
107486
107487 namespace JMS\Serializer\Tests\Fixtures\Doctrine;
107488
107489 use JMS\Serializer\Annotation\SerializedName;
107490 use JMS\Serializer\Annotation\XmlRoot;
107491 use JMS\Serializer\Annotation\XmlAttribute;
107492 use JMS\Serializer\Annotation\XmlList;
107493 use JMS\Serializer\Annotation\Groups;
107494 use JMS\Serializer\Annotation\Type;
107495 use Doctrine\Common\Collections\ArrayCollection;
107496 use Doctrine\ORM\Mapping as ORM;
107497
107498 /**
107499 * @ORM\Entity
107500 * @XmlRoot("blog-post")
107501 */
107502 class BlogPost
107503 {
107504 /**
107505 * @ORM\Id @ORM\Column(type="integer")
107506 */
107507 protected $id;
107508
107509 /**
107510 * @ORM\Column(type="string")
107511 * @Groups({"comments","post"})
107512 */
107513 private $title;
107514
107515 /**
107516 * @ORM\Column(type="some_custom_type")
107517 */
107518 protected $slug;
107519
107520 /**
107521 * @ORM\Column(type="datetime")
107522 * @XmlAttribute
107523 */
107524 private $createdAt;
107525
107526 /**
107527 * @ORM\Column(type="boolean")
107528 * @Type("integer")
107529 * This boolean to integer conversion is one of the few changes between this
107530 * and the standard BlogPost class. It's used to test the override behavior
107531 * of the DoctrineTypeDriver so notice it, but please don't change it.
107532 *
107533 * @SerializedName("is_published")
107534 * @Groups({"post"})
107535 * @XmlAttribute
107536 */
107537 private $published;
107538
107539 /**
107540 * @ORM\OneToMany(targetEntity="Comment", mappedBy="blogPost")
107541 * @XmlList(inline=true, entry="comment")
107542 * @Groups({"comments"})
107543 */
107544 private $comments;
107545
107546 /**
107547 * @ORM\OneToOne(targetEntity="Author")
107548 * @Groups({"post"})
107549 */
107550 private $author;
107551
107552 public function __construct($title, Author $author, \DateTime $createdAt)
107553 {
107554 $this->title = $title;
107555 $this->author = $author;
107556 $this->published = false;
107557 $this->comments = new ArrayCollection();
107558 $this->createdAt = $createdAt;
107559 }
107560
107561 public function setPublished()
107562 {
107563 $this->published = true;
107564 }
107565
107566 public function addComment(Comment $comment)
107567 {
107568 $this->comments->add($comment);
107569 }
107570 }
107571 <?php
107572
107573 /*
107574 * Copyright 2013 Johannes M. Schmitt <schmittjoh@gmail.com>
107575 *
107576 * Licensed under the Apache License, Version 2.0 (the "License");
107577 * you may not use this file except in compliance with the License.
107578 * You may obtain a copy of the License at
107579 *
107580 * http://www.apache.org/licenses/LICENSE-2.0
107581 *
107582 * Unless required by applicable law or agreed to in writing, software
107583 * distributed under the License is distributed on an "AS IS" BASIS,
107584 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
107585 * See the License for the specific language governing permissions and
107586 * limitations under the License.
107587 */
107588
107589 namespace JMS\Serializer\Tests\Fixtures\Doctrine;
107590
107591 use JMS\Serializer\Annotation\Type;
107592 use Doctrine\Common\Collections\ArrayCollection;
107593 use Doctrine\ORM\Mapping as ORM;
107594
107595 /** @ORM\Entity */
107596 class Comment
107597 {
107598 /**
107599 * @ORM\Id @ORM\Column(type="integer")
107600 */
107601 protected $id;
107602
107603 /**
107604 * @ORM\Column(type="Author")
107605 */
107606 private $author;
107607
107608 /** @ORM\ManyToOne(targetEntity="BlogPost") */
107609 private $blogPost;
107610
107611 /**
107612 * @ORM\Column(type="string")
107613 */
107614 private $text;
107615
107616 public function __construct(Author $author, $text)
107617 {
107618 $this->author = $author;
107619 $this->text = $text;
107620 $this->blogPost = new ArrayCollection();
107621 }
107622
107623 public function getAuthor()
107624 {
107625 return $this->author;
107626 }
107627 }
107628 <?php
107629
107630 /*
107631 * Copyright 2013 Johannes M. Schmitt <schmittjoh@gmail.com>
107632 *
107633 * Licensed under the Apache License, Version 2.0 (the "License");
107634 * you may not use this file except in compliance with the License.
107635 * You may obtain a copy of the License at
107636 *
107637 * http://www.apache.org/licenses/LICENSE-2.0
107638 *
107639 * Unless required by applicable law or agreed to in writing, software
107640 * distributed under the License is distributed on an "AS IS" BASIS,
107641 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
107642 * See the License for the specific language governing permissions and
107643 * limitations under the License.
107644 */
107645
107646 namespace JMS\Serializer\Tests\Fixtures\Doctrine;
107647
107648 use JMS\Serializer\Annotation\Type;
107649 use JMS\Serializer\Annotation\SerializedName;
107650
107651 use Doctrine\ORM\Mapping as ORM;
107652
107653 /** @ORM\Entity */
107654 class Author
107655 {
107656 /**
107657 * @ORM\Id @ORM\Column(type="integer")
107658 */
107659 protected $id;
107660
107661 /**
107662 * @ORM\Column(type="string")
107663 * @SerializedName("full_name")
107664 */
107665 private $name;
107666
107667 public function __construct($name)
107668 {
107669 $this->name = $name;
107670 }
107671
107672 public function getName()
107673 {
107674 return $this->name;
107675 }
107676 }
107677 <?php
107678
107679 /*
107680 * Copyright 2013 Johannes M. Schmitt <schmittjoh@gmail.com>
107681 *
107682 * Licensed under the Apache License, Version 2.0 (the "License");
107683 * you may not use this file except in compliance with the License.
107684 * You may obtain a copy of the License at
107685 *
107686 * http://www.apache.org/licenses/LICENSE-2.0
107687 *
107688 * Unless required by applicable law or agreed to in writing, software
107689 * distributed under the License is distributed on an "AS IS" BASIS,
107690 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
107691 * See the License for the specific language governing permissions and
107692 * limitations under the License.
107693 */
107694
107695 namespace JMS\Serializer\Tests\Fixtures;
107696
107697 use JMS\Serializer\Annotation\XmlRoot;
107698 use JMS\Serializer\Annotation\Type;
107699
107700 /** @XmlRoot("order") */
107701 class CurrencyAwareOrder
107702 {
107703 /** @Type("JMS\Serializer\Tests\Fixtures\CurrencyAwarePrice") */
107704 private $cost;
107705
107706 public function __construct(CurrencyAwarePrice $price = null)
107707 {
107708 $this->cost = $price ?: new CurrencyAwarePrice(5);
107709 }
107710 }<?php
107711
107712 /*
107713 * Copyright 2013 Johannes M. Schmitt <schmittjoh@gmail.com>
107714 *
107715 * Licensed under the Apache License, Version 2.0 (the "License");
107716 * you may not use this file except in compliance with the License.
107717 * You may obtain a copy of the License at
107718 *
107719 * http://www.apache.org/licenses/LICENSE-2.0
107720 *
107721 * Unless required by applicable law or agreed to in writing, software
107722 * distributed under the License is distributed on an "AS IS" BASIS,
107723 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
107724 * See the License for the specific language governing permissions and
107725 * limitations under the License.
107726 */
107727
107728 namespace JMS\Serializer\Tests\Twig;
107729
107730 use JMS\Serializer\SerializerInterface;
107731 use JMS\Serializer\Twig\SerializerExtension;
107732
107733 class SerializerExtensionTest extends \PHPUnit_Framework_TestCase
107734 {
107735 public function setUp()
107736 {
107737 $this->mockSerializer = $this->getMock('JMS\Serializer\SerializerInterface');
107738 }
107739
107740 public function testSerialize()
107741 {
107742 $obj = new \stdClass();
107743 $this->mockSerializer
107744 ->expects($this->once())
107745 ->method('serialize')
107746 ->with($this->equalTo($obj), $this->equalTo('json'));
107747 $serializerExtension = new SerializerExtension($this->mockSerializer);
107748 $serializerExtension->serialize($obj);
107749 }
107750 }
107751 <?php
107752
107753 /*
107754 * Copyright 2013 Johannes M. Schmitt <schmittjoh@gmail.com>
107755 *
107756 * Licensed under the Apache License, Version 2.0 (the "License");
107757 * you may not use this file except in compliance with the License.
107758 * You may obtain a copy of the License at
107759 *
107760 * http://www.apache.org/licenses/LICENSE-2.0
107761 *
107762 * Unless required by applicable law or agreed to in writing, software
107763 * distributed under the License is distributed on an "AS IS" BASIS,
107764 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
107765 * See the License for the specific language governing permissions and
107766 * limitations under the License.
107767 */
107768
107769 namespace JMS\Serializer\Tests\Exclusion;
107770
107771 use JMS\Serializer\Exclusion\DisjunctExclusionStrategy;
107772 use JMS\Serializer\Metadata\ClassMetadata;
107773 use JMS\Serializer\Metadata\PropertyMetadata;
107774 use JMS\Serializer\Metadata\StaticPropertyMetadata;
107775 use JMS\Serializer\SerializationContext;
107776
107777 class DisjunctExclusionStrategyTest extends \PHPUnit_Framework_TestCase
107778 {
107779 public function testShouldSkipClassShortCircuiting()
107780 {
107781 $metadata = new ClassMetadata('stdClass');
107782 $context = SerializationContext::create();
107783
107784 $strat = new DisjunctExclusionStrategy(array(
107785 $first = $this->getMock('JMS\Serializer\Exclusion\ExclusionStrategyInterface'),
107786 $last = $this->getMock('JMS\Serializer\Exclusion\ExclusionStrategyInterface'),
107787 ));
107788
107789 $first->expects($this->once())
107790 ->method('shouldSkipClass')
107791 ->with($metadata, $context)
107792 ->will($this->returnValue(true));
107793
107794 $last->expects($this->never())
107795 ->method('shouldSkipClass');
107796
107797 $this->assertTrue($strat->shouldSkipClass($metadata, $context));
107798 }
107799
107800 public function testShouldSkipClassDisjunctBehavior()
107801 {
107802 $metadata = new ClassMetadata('stdClass');
107803 $context = SerializationContext::create();
107804
107805 $strat = new DisjunctExclusionStrategy(array(
107806 $first = $this->getMock('JMS\Serializer\Exclusion\ExclusionStrategyInterface'),
107807 $last = $this->getMock('JMS\Serializer\Exclusion\ExclusionStrategyInterface'),
107808 ));
107809
107810 $first->expects($this->once())
107811 ->method('shouldSkipClass')
107812 ->with($metadata, $context)
107813 ->will($this->returnValue(false));
107814
107815 $last->expects($this->once())
107816 ->method('shouldSkipClass')
107817 ->with($metadata, $context)
107818 ->will($this->returnValue(true));
107819
107820 $this->assertTrue($strat->shouldSkipClass($metadata, $context));
107821 }
107822
107823 public function testShouldSkipClassReturnsFalseIfNoPredicateMatched()
107824 {
107825 $metadata = new ClassMetadata('stdClass');
107826 $context = SerializationContext::create();
107827
107828 $strat = new DisjunctExclusionStrategy(array(
107829 $first = $this->getMock('JMS\Serializer\Exclusion\ExclusionStrategyInterface'),
107830 $last = $this->getMock('JMS\Serializer\Exclusion\ExclusionStrategyInterface'),
107831 ));
107832
107833 $first->expects($this->once())
107834 ->method('shouldSkipClass')
107835 ->with($metadata, $context)
107836 ->will($this->returnValue(false));
107837
107838 $last->expects($this->once())
107839 ->method('shouldSkipClass')
107840 ->with($metadata, $context)
107841 ->will($this->returnValue(false));
107842
107843 $this->assertFalse($strat->shouldSkipClass($metadata, $context));
107844 }
107845
107846 public function testShouldSkipPropertyShortCircuiting()
107847 {
107848 $metadata = new StaticPropertyMetadata('stdClass', 'foo', 'bar');
107849 $context = SerializationContext::create();
107850
107851 $strat = new DisjunctExclusionStrategy(array(
107852 $first = $this->getMock('JMS\Serializer\Exclusion\ExclusionStrategyInterface'),
107853 $last = $this->getMock('JMS\Serializer\Exclusion\ExclusionStrategyInterface'),
107854 ));
107855
107856 $first->expects($this->once())
107857 ->method('shouldSkipProperty')
107858 ->with($metadata, $context)
107859 ->will($this->returnValue(true));
107860
107861 $last->expects($this->never())
107862 ->method('shouldSkipProperty');
107863
107864 $this->assertTrue($strat->shouldSkipProperty($metadata, $context));
107865 }
107866
107867 public function testShouldSkipPropertyDisjunct()
107868 {
107869 $metadata = new StaticPropertyMetadata('stdClass', 'foo', 'bar');
107870 $context = SerializationContext::create();
107871
107872 $strat = new DisjunctExclusionStrategy(array(
107873 $first = $this->getMock('JMS\Serializer\Exclusion\ExclusionStrategyInterface'),
107874 $last = $this->getMock('JMS\Serializer\Exclusion\ExclusionStrategyInterface'),
107875 ));
107876
107877 $first->expects($this->once())
107878 ->method('shouldSkipProperty')
107879 ->with($metadata, $context)
107880 ->will($this->returnValue(false));
107881
107882 $last->expects($this->once())
107883 ->method('shouldSkipProperty')
107884 ->with($metadata, $context)
107885 ->will($this->returnValue(true));
107886
107887 $this->assertTrue($strat->shouldSkipProperty($metadata, $context));
107888 }
107889
107890
107891 public function testShouldSkipPropertyReturnsFalseIfNoPredicateMatches()
107892 {
107893 $metadata = new StaticPropertyMetadata('stdClass', 'foo', 'bar');
107894 $context = SerializationContext::create();
107895
107896 $strat = new DisjunctExclusionStrategy(array(
107897 $first = $this->getMock('JMS\Serializer\Exclusion\ExclusionStrategyInterface'),
107898 $last = $this->getMock('JMS\Serializer\Exclusion\ExclusionStrategyInterface'),
107899 ));
107900
107901 $first->expects($this->once())
107902 ->method('shouldSkipProperty')
107903 ->with($metadata, $context)
107904 ->will($this->returnValue(false));
107905
107906 $last->expects($this->once())
107907 ->method('shouldSkipProperty')
107908 ->with($metadata, $context)
107909 ->will($this->returnValue(false));
107910
107911 $this->assertFalse($strat->shouldSkipProperty($metadata, $context));
107912 }
107913 }<?php
107914
107915 /*
107916 * Copyright 2013 Johannes M. Schmitt <schmittjoh@gmail.com>
107917 *
107918 * Licensed under the Apache License, Version 2.0 (the "License");
107919 * you may not use this file except in compliance with the License.
107920 * You may obtain a copy of the License at
107921 *
107922 * http://www.apache.org/licenses/LICENSE-2.0
107923 *
107924 * Unless required by applicable law or agreed to in writing, software
107925 * distributed under the License is distributed on an "AS IS" BASIS,
107926 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
107927 * See the License for the specific language governing permissions and
107928 * limitations under the License.
107929 */
107930
107931 namespace JMS\Serializer\Tests;
107932
107933 use JMS\Serializer\SerializerBuilder;
107934 use Symfony\Component\Filesystem\Filesystem;
107935 use JMS\Serializer\Handler\HandlerRegistry;
107936 use JMS\Serializer\JsonSerializationVisitor;
107937 use JMS\Serializer\Naming\CamelCaseNamingStrategy;
107938
107939 class SerializerBuilderTest extends \PHPUnit_Framework_TestCase
107940 {
107941 /** @var SerializerBuilder */
107942 private $builder;
107943 private $fs;
107944 private $tmpDir;
107945
107946 public function testBuildWithoutAnythingElse()
107947 {
107948 $serializer = $this->builder->build();
107949
107950 $this->assertEquals('"foo"', $serializer->serialize('foo', 'json'));
107951 $this->assertEquals('<?xml version="1.0" encoding="UTF-8"?>
107952 <result><![CDATA[foo]]></result>
107953 ', $serializer->serialize('foo', 'xml'));
107954 $this->assertEquals('foo
107955 ', $serializer->serialize('foo', 'yml'));
107956
107957 $this->assertEquals('foo', $serializer->deserialize('"foo"', 'string', 'json'));
107958 $this->assertEquals('foo', $serializer->deserialize('<?xml version="1.0" encoding="UTF-8"?><result><![CDATA[foo]]></result>', 'string', 'xml'));
107959 }
107960
107961 public function testWithCache()
107962 {
107963 $this->assertFileNotExists($this->tmpDir);
107964
107965 $this->assertSame($this->builder, $this->builder->setCacheDir($this->tmpDir));
107966 $serializer = $this->builder->build();
107967
107968 $this->assertFileExists($this->tmpDir);
107969 $this->assertFileExists($this->tmpDir.'/annotations');
107970 $this->assertFileExists($this->tmpDir.'/metadata');
107971
107972 $factory = $this->getField($serializer, 'factory');
107973 $this->assertAttributeSame(false, 'debug', $factory);
107974 $this->assertAttributeNotSame(null, 'cache', $factory);
107975 }
107976
107977 public function testDoesAddDefaultHandlers()
107978 {
107979 $serializer = $this->builder->build();
107980
107981 $this->assertEquals('"2020-04-16T00:00:00+0000"', $serializer->serialize(new \DateTime('2020-04-16', new \DateTimeZone('UTC')), 'json'));
107982 }
107983
107984 public function testDoesNotAddDefaultHandlersWhenExplicitlyConfigured()
107985 {
107986 $this->assertSame($this->builder, $this->builder->configureHandlers(function(HandlerRegistry $registry) {
107987 }));
107988
107989 $this->assertEquals('{}', $this->builder->build()->serialize(new \DateTime('2020-04-16'), 'json'));
107990 }
107991
107992 /**
107993 * @expectedException JMS\Serializer\Exception\UnsupportedFormatException
107994 * @expectedExceptionMessage The format "xml" is not supported for serialization.
107995 */
107996 public function testDoesNotAddOtherVisitorsWhenConfiguredExplicitly()
107997 {
107998 $this->assertSame(
107999 $this->builder,
108000 $this->builder->setSerializationVisitor('json', new JsonSerializationVisitor(new CamelCaseNamingStrategy()))
108001 );
108002
108003 $this->builder->build()->serialize('foo', 'xml');
108004 }
108005
108006 public function testIncludeInterfaceMetadata()
108007 {
108008 $this->assertFalse(
108009 $this->getIncludeInterfaces($this->builder),
108010 'Interface metadata are not included by default'
108011 );
108012
108013 $this->assertTrue(
108014 $this->getIncludeInterfaces($this->builder->includeInterfaceMetadata(true)),
108015 'Force including interface metadata'
108016 );
108017
108018 $this->assertFalse(
108019 $this->getIncludeInterfaces($this->builder->includeInterfaceMetadata(false)),
108020 'Force not including interface metadata'
108021 );
108022
108023 $this->assertSame(
108024 $this->builder,
108025 $this->builder->includeInterfaceMetadata(true)
108026 );
108027 }
108028
108029 protected function setUp()
108030 {
108031 $this->builder = SerializerBuilder::create();
108032 $this->fs = new Filesystem();
108033
108034 $this->tmpDir = sys_get_temp_dir().'/serializer';
108035 $this->fs->remove($this->tmpDir);
108036 clearstatcache();
108037 }
108038
108039 protected function tearDown()
108040 {
108041 $this->fs->remove($this->tmpDir);
108042 }
108043
108044 private function getField($obj, $name)
108045 {
108046 $ref = new \ReflectionProperty($obj, $name);
108047 $ref->setAccessible(true);
108048
108049 return $ref->getValue($obj);
108050 }
108051
108052 private function getIncludeInterfaces(SerializerBuilder $builder)
108053 {
108054 $factory = $this->getField($builder->build(), 'factory');
108055 return $this->getField($factory, 'includeInterfaces');
108056 }
108057 }
108058 <?php
108059
108060 /*
108061 * Copyright 2013 Johannes M. Schmitt <schmittjoh@gmail.com>
108062 *
108063 * Licensed under the Apache License, Version 2.0 (the "License");
108064 * you may not use this file except in compliance with the License.
108065 * You may obtain a copy of the License at
108066 *
108067 * http://www.apache.org/licenses/LICENSE-2.0
108068 *
108069 * Unless required by applicable law or agreed to in writing, software
108070 * distributed under the License is distributed on an "AS IS" BASIS,
108071 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
108072 * See the License for the specific language governing permissions and
108073 * limitations under the License.
108074 */
108075
108076 namespace JMS\Serializer\Tests\Serializer;
108077
108078 use JMS\Serializer\Handler\DateHandler;
108079
108080 class DateIntervalFormatTest extends \PHPUnit_Framework_TestCase
108081 {
108082 public function testFormat()
108083 {
108084 $dtf = new DateHandler();
108085
108086 $iso8601DateIntervalString = $dtf->format(new \DateInterval('PT45M'));
108087
108088 $this->assertEquals($iso8601DateIntervalString, 'PT45M');
108089
108090 $iso8601DateIntervalString = $dtf->format(new \DateInterval('P2YT45M'));
108091
108092 $this->assertEquals($iso8601DateIntervalString, 'P2YT45M');
108093
108094 $iso8601DateIntervalString = $dtf->format(new \DateInterval('P2Y4DT6H8M16S'));
108095
108096 $this->assertEquals($iso8601DateIntervalString, 'P2Y4DT6H8M16S');
108097 }
108098 }
108099 <?xml version="1.0" encoding="UTF-8"?>
108100 <author>
108101 <id>123</id>
108102 <full_name><![CDATA[Ruud Kamphuis]]></full_name>
108103 </author>
108104 <?xml version="1.0" encoding="UTF-8"?>
108105 <result>
108106 <hash/>
108107 </result>
108108 <?xml version="1.0" encoding="UTF-8"?>
108109 <result><![CDATA[xml-value]]></result>
108110 <?xml version="1.0" encoding="UTF-8"?>
108111 <result>1</result>
108112 <?xml version="1.0" encoding="UTF-8"?>
108113 <result>
108114 <bar><![CDATA[bar]]></bar>
108115 <none><![CDATA[none]]></none>
108116 </result>
108117 <?xml version="1.0" encoding="UTF-8"?>
108118 <result>
108119 <entry><![CDATA[This is the form error]]></entry>
108120 <entry><![CDATA[Another error]]></entry>
108121 </result>
108122 <?xml version="1.0" encoding="UTF-8"?>
108123 <result>1</result>
108124 <?xml version="1.0" encoding="UTF-8"?>
108125 <result xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance">
108126 <entry><![CDATA[bar]]></entry>
108127 <entry xsi:nil="true"/>
108128 <entry xsi:nil="true"/>
108129 </result>
108130 <?xml version="1.0" encoding="UTF-8"?>
108131 <form name="foo">
108132 <errors>
108133 <entry><![CDATA[This is the form error]]></entry>
108134 </errors>
108135 <form name="bar">
108136 <errors>
108137 <entry><![CDATA[Error of the child form]]></entry>
108138 </errors>
108139 </form>
108140 </form>
108141 <?xml version="1.0" encoding="UTF-8"?>
108142 <result>
108143 <entry>1</entry>
108144 <entry>3</entry>
108145 <entry>4</entry>
108146 </result>
108147 <?xml version="1.0" encoding="UTF-8"?>
108148 <result>
108149 <c><![CDATA[c]]></c>
108150 <a><![CDATA[a]]></a>
108151 <b><![CDATA[b]]></b>
108152 <d><![CDATA[d]]></d>
108153 </result>
108154 <?xml version="1.0" encoding="UTF-8"?>
108155 <result>
108156 <array>
108157 <key-one><![CDATA[foo]]></key-one>
108158 <key-two>1</key-two>
108159 <nested-array>
108160 <bar><![CDATA[foo]]></bar>
108161 </nested-array>
108162 <without-keys>
108163 <entry>1</entry>
108164 <entry><![CDATA[test]]></entry>
108165 </without-keys>
108166 <mixed>
108167 <entry><![CDATA[test]]></entry>
108168 <foo><![CDATA[bar]]></foo>
108169 <entry><![CDATA[bar]]></entry>
108170 </mixed>
108171 <entry><![CDATA[foo]]></entry>
108172 </array>
108173 </result>
108174 <?xml version="1.0" encoding="UTF-8"?>
108175 <result><![CDATA[PT45M]]></result>
108176 <?xml version="1.0" encoding="UTF-8"?>
108177 <result>
108178 <foo><![CDATA[foo]]></foo>
108179 <foobar><![CDATA[foobar]]></foobar>
108180 </result>
108181 <?xml version="1.0" encoding="UTF-8"?>
108182 <result>
108183 <entry>1.34</entry>
108184 <entry>3</entry>
108185 <entry>6.42</entry>
108186 </result>
108187 <?xml version="1.0" encoding="UTF-8"?>
108188 <result>
108189 <text><![CDATA[foo]]></text>
108190 </result>
108191 <?xml version="1.0" encoding="UTF-8"?>
108192 <result>
108193 <foo><![CDATA[foo]]></foo>
108194 <moo><![CDATA[bar]]></moo>
108195 <camel_case><![CDATA[boo]]></camel_case>
108196 </result>
108197 <?xml version="1.0" encoding="UTF-8"?>
108198 <result>
108199 <collection>
108200 <entry>
108201 <name><![CDATA[child1]]></name>
108202 </entry>
108203 <entry>
108204 <name><![CDATA[child2]]></name>
108205 </entry>
108206 </collection>
108207 <another_collection>
108208 <entry>
108209 <name><![CDATA[child1]]></name>
108210 </entry>
108211 <entry>
108212 <name><![CDATA[child2]]></name>
108213 </entry>
108214 </another_collection>
108215 </result>
108216 <?xml version="1.0" encoding="UTF-8"?>
108217 <result>
108218 <km>5</km>
108219 <type><![CDATA[car]]></type>
108220 </result>
108221 <?xml version="1.0" encoding="UTF-8"?>
108222 <result xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xsi:nil="true"/>
108223 <?xml version="1.0" encoding="UTF-8"?>
108224 <result>false</result>
108225 <?xml version="1.0" encoding="UTF-8"?>
108226 <result>
108227 <c><![CDATA[c]]></c>
108228 <d><![CDATA[d]]></d>
108229 <a><![CDATA[a]]></a>
108230 <b><![CDATA[b]]></b>
108231 </result>
108232 <?xml version="1.0" encoding="UTF-8"?>
108233 <price currency="EUR">2.34</price>
108234 <?xml version="1.0" encoding="UTF-8"?>
108235 <result>
108236 <foo><![CDATA[foo]]></foo>
108237 <foobar><![CDATA[foobar]]></foobar>
108238 <bar><![CDATA[bar]]></bar>
108239 </result>
108240 <?xml version="1.0" encoding="UTF-8"?>
108241 <result foo="bar"/>
108242 <?xml version="1.0" encoding="UTF-8"?>
108243 <result>
108244 <foo><![CDATA[c]]></foo>
108245 <b><![CDATA[b]]></b>
108246 <a><![CDATA[a]]></a>
108247 </result>
108248 <?xml version="1.0" encoding="UTF-8"?>
108249 <result>
108250 <a><![CDATA[a]]></a>
108251 <b><![CDATA[b]]></b>
108252 </result>
108253 <?xml version="1.0" encoding="UTF-8"?>
108254 <result>
108255 <name><![CDATA[Foo Bar]]></name>
108256 </result>
108257 <?xml version="1.0" encoding="UTF-8"?>
108258 <result>
108259 <tree>
108260 <children>
108261 <entry>
108262 <children>
108263 <entry>
108264 <children>
108265 <entry/>
108266 </children>
108267 <foo><![CDATA[bar]]></foo>
108268 </entry>
108269 </children>
108270 <foo><![CDATA[bar]]></foo>
108271 </entry>
108272 </children>
108273 <foo><![CDATA[bar]]></foo>
108274 </tree>
108275 </result>
108276 <?xml version="1.0" encoding="UTF-8"?>
108277 <result><![CDATA[2011-08-30T00:00:00+0000]]></result>
108278 <?xml version="1.0" encoding="UTF-8"?>
108279 <result>
108280 <high>8</high>
108281 </result>
108282 <?xml version="1.0" encoding="UTF-8"?>
108283 <result>
108284 <foo><![CDATA[foo]]></foo>
108285 <moo><![CDATA[bar]]></moo>
108286 <camel_case><![CDATA[proxy-boo]]></camel_case>
108287 </result>
108288 <?xml version="1.0" encoding="UTF-8"?>
108289 <order>
108290 <cost currency="EUR">1.23</cost>
108291 </order>
108292 <?xml version="1.0" encoding="UTF-8"?>
108293 <result>
108294 <entry>
108295 <foo><![CDATA[foo]]></foo>
108296 <moo><![CDATA[bar]]></moo>
108297 <camel_case><![CDATA[boo]]></camel_case>
108298 </entry>
108299 <entry>
108300 <foo><![CDATA[baz]]></foo>
108301 <moo><![CDATA[boo]]></moo>
108302 <camel_case><![CDATA[boo]]></camel_case>
108303 </entry>
108304 </result>
108305 <?xml version="1.0" encoding="UTF-8"?>
108306 <input type="text" name="firstname" value="Adrien"/>
108307 <?xml version="1.0" encoding="UTF-8"?>
108308 <result>
108309 <entry><![CDATA[foo]]></entry>
108310 <entry>1</entry>
108311 <entry>true</entry>
108312 <entry>
108313 <foo><![CDATA[foo]]></foo>
108314 <moo><![CDATA[bar]]></moo>
108315 <camel_case><![CDATA[boo]]></camel_case>
108316 </entry>
108317 <entry>
108318 <entry>1</entry>
108319 <entry>3</entry>
108320 <entry>true</entry>
108321 </entry>
108322 </result>
108323 <?xml version="1.0" encoding="UTF-8"?>
108324 <blog-post created_at="2011-07-30T00:00:00+0000" is_published="false">
108325 <title><![CDATA[This is a nice title.]]></title>
108326 <comment>
108327 <author>
108328 <full_name><![CDATA[Foo Bar]]></full_name>
108329 </author>
108330 <text><![CDATA[foo]]></text>
108331 </comment>
108332 <comment2>
108333 <author>
108334 <full_name><![CDATA[Foo Bar]]></full_name>
108335 </author>
108336 <text><![CDATA[foo]]></text>
108337 </comment2>
108338 <metadata>
108339 <entry key="foo"><![CDATA[bar]]></entry>
108340 </metadata>
108341 <author>
108342 <full_name><![CDATA[Foo Bar]]></full_name>
108343 </author>
108344 </blog-post>
108345 <?xml version="1.0" encoding="UTF-8"?>
108346 <blog-post created_at="2011-07-30T00:00:00+0000" is_published="false">
108347 <title><![CDATA[This is a nice title.]]></title>
108348 <author xsi:nil="true"/>
108349 </blog-post>
108350 <?xml version="1.0" encoding="UTF-8"?>
108351 <person_collection>
108352 <person age="28"><![CDATA[Matthias Noback]]></person>
108353 <location><![CDATA[The Netherlands]]></location>
108354 </person_collection>
108355 <?xml version="1.0" encoding="UTF-8"?>
108356 <result>
108357 <foo><![CDATA[foo]]></foo>
108358 <foobar><![CDATA[foobar]]></foobar>
108359 <bar><![CDATA[bar]]></bar>
108360 <none><![CDATA[none]]></none>
108361 </result>
108362 <?xml version="1.0" encoding="UTF-8"?>
108363 <result>
108364 <low>1</low>
108365 <high>8</high>
108366 </result>
108367 <?xml version="1.0" encoding="UTF-8"?>
108368 <post>
108369 <comments author-name="Foo" count="2">
108370 <comment>
108371 <author>
108372 <full_name><![CDATA[Foo]]></full_name>
108373 </author>
108374 <text><![CDATA[foo]]></text>
108375 </comment>
108376 <comment>
108377 <author>
108378 <full_name><![CDATA[Foo]]></full_name>
108379 </author>
108380 <text><![CDATA[bar]]></text>
108381 </comment>
108382 </comments>
108383 </post>
108384 <?xml version="1.0" encoding="UTF-8"?>
108385 <log>
108386 <author_list>
108387 <entry _key="0">
108388 <full_name><![CDATA[Johannes Schmitt]]></full_name>
108389 </entry>
108390 <entry _key="1">
108391 <full_name><![CDATA[John Doe]]></full_name>
108392 </entry>
108393 </author_list>
108394 <comment>
108395 <author>
108396 <full_name><![CDATA[Foo Bar]]></full_name>
108397 </author>
108398 <text><![CDATA[foo]]></text>
108399 </comment>
108400 <comment>
108401 <author>
108402 <full_name><![CDATA[Foo Bar]]></full_name>
108403 </author>
108404 <text><![CDATA[bar]]></text>
108405 </comment>
108406 <comment>
108407 <author>
108408 <full_name><![CDATA[Foo Bar]]></full_name>
108409 </author>
108410 <text><![CDATA[baz]]></text>
108411 </comment>
108412 </log>
108413 <?xml version="1.0" encoding="UTF-8"?>
108414 <result>4.533</result>
108415 <?xml version="1.0" encoding="UTF-8"?>
108416 <violation property_path="foo">
108417 <message><![CDATA[Message of violation]]></message>
108418 </violation>
108419 <?xml version="1.0" encoding="UTF-8"?>
108420 <result xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance">
108421 <foo><![CDATA[foo]]></foo>
108422 <moo><![CDATA[bar]]></moo>
108423 <camel_case><![CDATA[boo]]></camel_case>
108424 <null_property xsi:nil="true"/>
108425 </result>
108426 <?xml version="1.0" encoding="UTF-8"?>
108427 <result>
108428 <id>1</id>
108429 <name><![CDATA[Johannes]]></name>
108430 <read_only_property>42</read_only_property>
108431 </result>
108432 <?xml version="1.0" encoding="UTF-8"?>
108433 <order>
108434 <cost>12.34</cost>
108435 </order>
108436 <?xml version="1.0" encoding="UTF-8"?>
108437 <result>
108438 <violation property_path="foo">
108439 <message><![CDATA[Message of violation]]></message>
108440 </violation>
108441 <violation property_path="bar">
108442 <message><![CDATA[Message of another violation]]></message>
108443 </violation>
108444 </result>
108445 <?xml version="1.0" encoding="UTF-8"?>
108446 <result>
108447 <entry>true</entry>
108448 <entry>false</entry>
108449 </result>
108450 <?xml version="1.0" encoding="UTF-8"?>
108451 <custom>serialized</custom>
108452 <?xml version="1.0" encoding="UTF-8"?>
108453 <result>
108454 <map>
108455 <entry key="key-one"><![CDATA[One]]></entry>
108456 <entry key="key-two"><![CDATA[Two]]></entry>
108457 </map>
108458 </result>
108459 <?xml version="1.0" encoding="UTF-8"?>
108460 <result>
108461 <val><![CDATA[One]]></val>
108462 <val><![CDATA[Two]]></val>
108463 </result>
108464 <?xml version="1.0" encoding="UTF-8"?>
108465 <price>3</price>
108466 <?xml version="1.0" encoding="UTF-8"?>
108467 <result>
108468 <exist_field><![CDATA[value]]></exist_field>
108469 <test><![CDATA[other-name]]></test>
108470 <virtual_value><![CDATA[value]]></virtual_value>
108471 <typed_virtual_property>1</typed_virtual_property>
108472 </result>
108473 <?xml version="1.0" encoding="UTF-8"?>
108474 <result>
108475 <entry><![CDATA[foo]]></entry>
108476 <entry><![CDATA[bar]]></entry>
108477 </result>
108478 <?xml version="1.0" encoding="UTF-8"?>
108479 <result>true</result>
108480 <?xml version="1.0" encoding="UTF-8"?>
108481 <result>
108482 <low>1</low>
108483 </result>
108484 <?xml version="1.0" encoding="UTF-8"?>
108485 <person_location>
108486 <person age="28"><![CDATA[Matthias Noback]]></person>
108487 <location><![CDATA[The Netherlands]]></location>
108488 </person_location>
108489 <?xml version="1.0" encoding="UTF-8"?>
108490 <result xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance">
108491 <author xsi:nil="true"/>
108492 <text><![CDATA[foo]]></text>
108493 </result>
108494 <?xml version="1.0" encoding="UTF-8"?>
108495 <result><![CDATA[foo]]></result>
108496 <?xml version="1.0" encoding="UTF-8"?>
108497 <result>
108498 <km>5</km>
108499 </result>
108500 <?php
108501
108502 /*
108503 * Copyright 2013 Johannes M. Schmitt <schmittjoh@gmail.com>
108504 *
108505 * Licensed under the Apache License, Version 2.0 (the "License");
108506 * you may not use this file except in compliance with the License.
108507 * You may obtain a copy of the License at
108508 *
108509 * http://www.apache.org/licenses/LICENSE-2.0
108510 *
108511 * Unless required by applicable law or agreed to in writing, software
108512 * distributed under the License is distributed on an "AS IS" BASIS,
108513 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
108514 * See the License for the specific language governing permissions and
108515 * limitations under the License.
108516 */
108517
108518 namespace JMS\Serializer\Tests\Serializer;
108519
108520 use JMS\Serializer\Construction\UnserializeObjectConstructor;
108521 use JMS\Serializer\Handler\HandlerRegistry;
108522 use JMS\Serializer\EventDispatcher\EventDispatcher;
108523 use Doctrine\Common\Annotations\AnnotationReader;
108524 use JMS\Serializer\Metadata\Driver\AnnotationDriver;
108525 use JMS\Serializer\GraphNavigator;
108526 use Metadata\MetadataFactory;
108527
108528 class GraphNavigatorTest extends \PHPUnit_Framework_TestCase
108529 {
108530 private $metadataFactory;
108531 private $handlerRegistry;
108532 private $objectConstructor;
108533 private $dispatcher;
108534 private $navigator;
108535 private $context;
108536
108537 /**
108538 * @expectedException JMS\Serializer\Exception\RuntimeException
108539 * @expectedExceptionMessage Resources are not supported in serialized data.
108540 */
108541 public function testResourceThrowsException()
108542 {
108543 $this->context->expects($this->any())
108544 ->method('getDirection')
108545 ->will($this->returnValue(GraphNavigator::DIRECTION_SERIALIZATION));
108546
108547 $this->navigator->accept(STDIN, null, $this->context);
108548 }
108549
108550 public function testNavigatorPassesInstanceOnSerialization()
108551 {
108552 $object = new SerializableClass;
108553 $metadata = $this->metadataFactory->getMetadataForClass(get_class($object));
108554
108555 $self = $this;
108556 $context = $this->context;
108557 $exclusionStrategy = $this->getMock('JMS\Serializer\Exclusion\ExclusionStrategyInterface');
108558 $exclusionStrategy->expects($this->once())
108559 ->method('shouldSkipClass')
108560 ->will($this->returnCallback(function($passedMetadata, $passedContext) use ($metadata, $context, $self) {
108561 $self->assertSame($metadata, $passedMetadata);
108562 $self->assertSame($context, $passedContext);
108563 }));
108564 $exclusionStrategy->expects($this->once())
108565 ->method('shouldSkipProperty')
108566 ->will($this->returnCallback(function($propertyMetadata, $passedContext) use ($context, $metadata, $self) {
108567 $self->assertSame($metadata->propertyMetadata['foo'], $propertyMetadata);
108568 $self->assertSame($context, $passedContext);
108569 }));
108570
108571 $this->context->expects($this->once())
108572 ->method('getExclusionStrategy')
108573 ->will($this->returnValue($exclusionStrategy));
108574
108575 $this->context->expects($this->any())
108576 ->method('getDirection')
108577 ->will($this->returnValue(GraphNavigator::DIRECTION_SERIALIZATION));
108578
108579 $this->context->expects($this->any())
108580 ->method('getVisitor')
108581 ->will($this->returnValue($this->getMock('JMS\Serializer\VisitorInterface')));
108582
108583 $this->navigator = new GraphNavigator($this->metadataFactory, $this->handlerRegistry, $this->objectConstructor, $this->dispatcher);
108584 $this->navigator->accept($object, null, $this->context);
108585 }
108586
108587 public function testNavigatorPassesNullOnDeserialization()
108588 {
108589 $class = __NAMESPACE__.'\SerializableClass';
108590 $metadata = $this->metadataFactory->getMetadataForClass($class);
108591
108592 $context = $this->context;
108593 $exclusionStrategy = $this->getMock('JMS\Serializer\Exclusion\ExclusionStrategyInterface');
108594 $exclusionStrategy->expects($this->once())
108595 ->method('shouldSkipClass')
108596 ->with($metadata, $this->callback(function ($navigatorContext) use ($context) {
108597 return $navigatorContext === $context;
108598 }));
108599
108600 $exclusionStrategy->expects($this->once())
108601 ->method('shouldSkipProperty')
108602 ->with($metadata->propertyMetadata['foo'], $this->callback(function ($navigatorContext) use ($context) {
108603 return $navigatorContext === $context;
108604 }));
108605
108606 $this->context->expects($this->once())
108607 ->method('getExclusionStrategy')
108608 ->will($this->returnValue($exclusionStrategy));
108609
108610 $this->context->expects($this->any())
108611 ->method('getDirection')
108612 ->will($this->returnValue(GraphNavigator::DIRECTION_DESERIALIZATION));
108613
108614 $this->context->expects($this->any())
108615 ->method('getVisitor')
108616 ->will($this->returnValue($this->getMock('JMS\Serializer\VisitorInterface')));
108617
108618 $this->navigator = new GraphNavigator($this->metadataFactory, $this->handlerRegistry, $this->objectConstructor, $this->dispatcher);
108619 $this->navigator->accept('random', array('name' => $class, 'params' => array()), $this->context);
108620 }
108621
108622 public function testNavigatorChangeTypeOnSerialization()
108623 {
108624 $object = new SerializableClass;
108625 $typeName = 'JsonSerializable';
108626
108627 $this->dispatcher->addListener('serializer.pre_serialize', function($event) use ($typeName) {
108628 $type = $event->getType();
108629 $type['name'] = $typeName;
108630 $event->setType($type['name'], $type['params']);
108631 });
108632
108633 $subscribingHandlerClass = $this->getMockClass('JMS\Serializer\Handler\SubscribingHandlerInterface', array('getSubscribingMethods', 'serialize'));
108634 $subscribingHandlerClass::staticExpects($this->once())
108635 ->method('getSubscribingMethods')
108636 ->will($this->returnValue(array(array(
108637 'type' => $typeName,
108638 'format' => 'foo',
108639 'direction' => GraphNavigator::DIRECTION_SERIALIZATION,
108640 'method' => 'serialize'
108641 ))));
108642
108643 $subscribingHandler = new $subscribingHandlerClass();
108644 $subscribingHandler->expects($this->once())
108645 ->method('serialize')
108646 ->with($this->equalTo($this->context), $this->equalTo($object));
108647
108648 $this->handlerRegistry->registerSubscribingHandler($subscribingHandler);
108649
108650 $this->context->expects($this->any())
108651 ->method('getDirection')
108652 ->will($this->returnValue(GraphNavigator::DIRECTION_SERIALIZATION));
108653
108654 $this->context->expects($this->any())
108655 ->method('getVisitor')
108656 ->will($this->returnValue($this->getMock('JMS\Serializer\VisitorInterface')));
108657
108658 $this->navigator = new GraphNavigator($this->metadataFactory, $this->handlerRegistry, $this->objectConstructor, $this->dispatcher);
108659 $this->navigator->accept($object, null, $this->context);
108660 }
108661
108662 protected function setUp()
108663 {
108664 $this->context = $this->getMock('JMS\Serializer\Context');
108665 $this->dispatcher = new EventDispatcher();
108666 $this->handlerRegistry = new HandlerRegistry();
108667 $this->objectConstructor = new UnserializeObjectConstructor();
108668
108669 $this->metadataFactory = new MetadataFactory(new AnnotationDriver(new AnnotationReader()));
108670 $this->navigator = new GraphNavigator($this->metadataFactory, $this->handlerRegistry, $this->objectConstructor, $this->dispatcher);
108671 }
108672 }
108673
108674 class SerializableClass
108675 {
108676 public $foo = 'bar';
108677 }
108678 <?php
108679
108680 /*
108681 * Copyright 2013 Johannes M. Schmitt <schmittjoh@gmail.com>
108682 *
108683 * Licensed under the Apache License, Version 2.0 (the "License");
108684 * you may not use this file except in compliance with the License.
108685 * You may obtain a copy of the License at
108686 *
108687 * http://www.apache.org/licenses/LICENSE-2.0
108688 *
108689 * Unless required by applicable law or agreed to in writing, software
108690 * distributed under the License is distributed on an "AS IS" BASIS,
108691 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
108692 * See the License for the specific language governing permissions and
108693 * limitations under the License.
108694 */
108695
108696 namespace JMS\Serializer\Tests\Serializer;
108697
108698 use JMS\Serializer\Exception\RuntimeException;
108699
108700 class YamlSerializationTest extends BaseSerializationTest
108701 {
108702 public function testConstraintViolation()
108703 {
108704 $this->markTestSkipped('This is not available for the YAML format.');
108705 }
108706
108707 public function testConstraintViolationList()
108708 {
108709 $this->markTestSkipped('This is not available for the YAML format.');
108710 }
108711
108712 public function testFormErrors()
108713 {
108714 $this->markTestSkipped('This is not available for the YAML format.');
108715 }
108716
108717 public function testNestedFormErrors()
108718 {
108719 $this->markTestSkipped('This is not available for the YAML format.');
108720 }
108721
108722 protected function getContent($key)
108723 {
108724 if (!file_exists($file = __DIR__.'/yml/'.$key.'.yml')) {
108725 throw new RuntimeException(sprintf('The content with key "%s" does not exist.', $key));
108726 }
108727
108728 return file_get_contents($file);
108729 }
108730
108731 protected function getFormat()
108732 {
108733 return 'yml';
108734 }
108735
108736 protected function hasDeserializer()
108737 {
108738 return false;
108739 }
108740 }<?php
108741
108742 /*
108743 * Copyright 2013 Johannes M. Schmitt <schmittjoh@gmail.com>
108744 *
108745 * Licensed under the Apache License, Version 2.0 (the "License");
108746 * you may not use this file except in compliance with the License.
108747 * You may obtain a copy of the License at
108748 *
108749 * http://www.apache.org/licenses/LICENSE-2.0
108750 *
108751 * Unless required by applicable law or agreed to in writing, software
108752 * distributed under the License is distributed on an "AS IS" BASIS,
108753 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
108754 * See the License for the specific language governing permissions and
108755 * limitations under the License.
108756 */
108757
108758 namespace JMS\Serializer\Tests\Serializer;
108759
108760 use JMS\Serializer\TypeParser;
108761
108762 class TypeParserTest extends \PHPUnit_Framework_TestCase
108763 {
108764 private $parser;
108765
108766 /**
108767 * @dataProvider getTypes
108768 */
108769 public function testParse($type, $name, array $params = array())
108770 {
108771 $this->assertEquals(array('name' => $name, 'params' => $params), $this->parser->parse($type));
108772 }
108773
108774 public function getTypes()
108775 {
108776 $types = array();
108777 $types[] = array('string', 'string');
108778 $types[] = array('array<Foo>', 'array', array(array('name' => 'Foo', 'params' => array())));
108779 $types[] = array('array<Foo,Bar>', 'array', array(array('name' => 'Foo', 'params' => array()), array('name' => 'Bar', 'params' => array())));
108780 $types[] = array('array<Foo\Bar, Baz\Boo>', 'array', array(array('name' => 'Foo\Bar', 'params' => array()), array('name' => 'Baz\Boo', 'params' => array())));
108781 $types[] = array('a<b<c,d>,e>', 'a', array(array('name' => 'b', 'params' => array(array('name' => 'c', 'params' => array()), array('name' => 'd', 'params' => array()))), array('name' => 'e', 'params' => array())));
108782 $types[] = array('Foo', 'Foo');
108783 $types[] = array('Foo\Bar', 'Foo\Bar');
108784 $types[] = array('Foo<"asdf asdf">', 'Foo', array('asdf asdf'));
108785
108786 return $types;
108787 }
108788
108789 /**
108790 * @expectedException \JMS\Parser\SyntaxErrorException
108791 * @expectedExceptionMessage Expected T_CLOSE_BRACKET, but got end of input.
108792 */
108793 public function testParamTypeMustEndWithBracket()
108794 {
108795 $this->parser->parse('Foo<bar');
108796 }
108797
108798 /**
108799 * @expectedException \JMS\Parser\SyntaxErrorException
108800 * @expectedExceptionMessage Expected T_NAME, but got "," of type T_COMMA at beginning of input.
108801 */
108802 public function testMustStartWithName()
108803 {
108804 $this->parser->parse(',');
108805 }
108806
108807 /**
108808 * @expectedException \JMS\Parser\SyntaxErrorException
108809 * @expectedExceptionMessage Expected any of T_NAME or T_STRING, but got ">" of type T_CLOSE_BRACKET at position 4 (0-based).
108810 */
108811 public function testEmptyParams()
108812 {
108813 $this->parser->parse('Foo<>');
108814 }
108815
108816 /**
108817 * @expectedException \JMS\Parser\SyntaxErrorException
108818 * @expectedExceptionMessage Expected any of T_NAME or T_STRING, but got ">" of type T_CLOSE_BRACKET at position 7 (0-based).
108819 */
108820 public function testNoTrailingComma()
108821 {
108822 $this->parser->parse('Foo<aa,>');
108823 }
108824
108825 protected function setUp()
108826 {
108827 $this->parser = new TypeParser();
108828 }
108829 }<?php
108830
108831 /*
108832 * Copyright 2013 Johannes M. Schmitt <schmittjoh@gmail.com>
108833 *
108834 * Licensed under the Apache License, Version 2.0 (the "License");
108835 * you may not use this file except in compliance with the License.
108836 * You may obtain a copy of the License at
108837 *
108838 * http://www.apache.org/licenses/LICENSE-2.0
108839 *
108840 * Unless required by applicable law or agreed to in writing, software
108841 * distributed under the License is distributed on an "AS IS" BASIS,
108842 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
108843 * See the License for the specific language governing permissions and
108844 * limitations under the License.
108845 */
108846
108847 namespace JMS\Serializer\Tests\Serializer;
108848
108849 use JMS\Serializer\Context;
108850 use JMS\Serializer\SerializationContext;
108851 use JMS\Serializer\Tests\Fixtures\InvalidUsageOfXmlValue;
108852 use JMS\Serializer\Exception\InvalidArgumentException;
108853 use JMS\Serializer\Tests\Fixtures\PersonCollection;
108854 use JMS\Serializer\Tests\Fixtures\PersonLocation;
108855 use JMS\Serializer\Tests\Fixtures\Person;
108856 use JMS\Serializer\Tests\Fixtures\ObjectWithVirtualXmlProperties;
108857 use JMS\Serializer\Tests\Fixtures\ObjectWithXmlKeyValuePairs;
108858 use JMS\Serializer\Tests\Fixtures\Input;
108859
108860 class XmlSerializationTest extends BaseSerializationTest
108861 {
108862 /**
108863 * @expectedException JMS\Serializer\Exception\RuntimeException
108864 */
108865 public function testInvalidUsageOfXmlValue()
108866 {
108867 $obj = new InvalidUsageOfXmlValue();
108868 $this->serialize($obj);
108869 }
108870
108871 public function testPropertyIsObjectWithAttributeAndValue()
108872 {
108873 $personCollection = new PersonLocation;
108874 $person = new Person;
108875 $person->name = 'Matthias Noback';
108876 $person->age = 28;
108877 $personCollection->person = $person;
108878 $personCollection->location = 'The Netherlands';
108879
108880 $this->assertEquals($this->getContent('person_location'), $this->serialize($personCollection));
108881 }
108882
108883 public function testPropertyIsCollectionOfObjectsWithAttributeAndValue()
108884 {
108885 $personCollection = new PersonCollection;
108886 $person = new Person;
108887 $person->name = 'Matthias Noback';
108888 $person->age = 28;
108889 $personCollection->persons->add($person);
108890 $personCollection->location = 'The Netherlands';
108891
108892 $this->assertEquals($this->getContent('person_collection'), $this->serialize($personCollection));
108893 }
108894
108895 /**
108896 * @expectedException JMS\Serializer\Exception\InvalidArgumentException
108897 * @expectedExceptionMessage The document type "<!DOCTYPE author [<!ENTITY foo SYSTEM "php://filter/read=convert.base64-encode/resource=XmlSerializationTest.php">]>" is not allowed. If it is safe, you may add it to the whitelist configuration.
108898 */
108899 public function testExternalEntitiesAreDisabledByDefault()
108900 {
108901 $this->deserialize('<?xml version="1.0"?>
108902 <!DOCTYPE author [
108903 <!ENTITY foo SYSTEM "php://filter/read=convert.base64-encode/resource='.basename(__FILE__).'">
108904 ]>
108905 <result>
108906 &foo;
108907 </result>', 'stdClass');
108908 }
108909
108910 /**
108911 * @expectedException JMS\Serializer\Exception\InvalidArgumentException
108912 * @expectedExceptionMessage The document type "<!DOCTYPE foo>" is not allowed. If it is safe, you may add it to the whitelist configuration.
108913 */
108914 public function testDocumentTypesAreNotAllowed()
108915 {
108916 $this->deserialize('<?xml version="1.0"?><!DOCTYPE foo><foo></foo>', 'stdClass');
108917 }
108918
108919 public function testWhitelistedDocumentTypesAreAllowed()
108920 {
108921 $this->deserializationVisitors->get('xml')->get()->setDoctypeWhitelist(array(
108922 '<!DOCTYPE authorized SYSTEM "http://authorized_url.dtd">',
108923 '<!DOCTYPE author [<!ENTITY foo SYSTEM "php://filter/read=convert.base64-encode/resource='.basename(__FILE__).'">]>'));
108924
108925 $this->serializer->deserialize('<?xml version="1.0"?>
108926 <!DOCTYPE authorized SYSTEM "http://authorized_url.dtd">
108927 <foo></foo>', 'stdClass', 'xml');
108928
108929 $this->serializer->deserialize('<?xml version="1.0"?>
108930 <!DOCTYPE author [
108931 <!ENTITY foo SYSTEM "php://filter/read=convert.base64-encode/resource='.basename(__FILE__).'">
108932 ]>
108933 <foo></foo>', 'stdClass', 'xml');
108934 }
108935
108936 public function testVirtualAttributes()
108937 {
108938 $this->assertEquals(
108939 $this->getContent('virtual_attributes'),
108940 $this->serialize(new ObjectWithVirtualXmlProperties(), SerializationContext::create()->setGroups(array('attributes')))
108941 );
108942 }
108943
108944 public function testVirtualValues()
108945 {
108946 $this->assertEquals(
108947 $this->getContent('virtual_values'),
108948 $this->serialize(new ObjectWithVirtualXmlProperties(), SerializationContext::create()->setGroups(array('values')))
108949 );
108950 }
108951
108952 public function testVirtualXmlList()
108953 {
108954 $this->assertEquals(
108955 $this->getContent('virtual_properties_list'),
108956 $this->serialize(new ObjectWithVirtualXmlProperties(), SerializationContext::create()->setGroups(array('list')))
108957 );
108958 }
108959
108960 public function testVirtualXmlMap()
108961 {
108962 $this->assertEquals(
108963 $this->getContent('virtual_properties_map'),
108964 $this->serialize(new ObjectWithVirtualXmlProperties(), SerializationContext::create()->setGroups(array('map')))
108965 );
108966 }
108967
108968 public function testArrayKeyValues()
108969 {
108970 $this->assertEquals($this->getContent('array_key_values'), $this->serializer->serialize(new ObjectWithXmlKeyValuePairs(), 'xml'));
108971 }
108972
108973 /**
108974 * @expectedException JMS\Serializer\Exception\RuntimeException
108975 * @expectedExceptionMessage Unsupported value type for XML attribute map. Expected array but got object
108976 */
108977 public function testXmlAttributeMapWithoutArray()
108978 {
108979 $attributes = new \ArrayObject(array(
108980 'type' => 'text',
108981 ));
108982
108983 $this->serializer->serialize(new Input($attributes), $this->getFormat());
108984 }
108985
108986 public function testDeserializingNull()
108987 {
108988 $this->markTestSkipped('Not supported in XML.');
108989 }
108990
108991 /**
108992 * @param string $key
108993 */
108994 protected function getContent($key)
108995 {
108996 if (!file_exists($file = __DIR__.'/xml/'.$key.'.xml')) {
108997 throw new InvalidArgumentException(sprintf('The key "%s" is not supported.', $key));
108998 }
108999
109000 return file_get_contents($file);
109001 }
109002
109003 protected function getFormat()
109004 {
109005 return 'xml';
109006 }
109007 }
109008 <?php
109009
109010 /*
109011 * Copyright 2013 Johannes M. Schmitt <schmittjoh@gmail.com>
109012 *
109013 * Licensed under the Apache License, Version 2.0 (the "License");
109014 * you may not use this file except in compliance with the License.
109015 * You may obtain a copy of the License at
109016 *
109017 * http://www.apache.org/licenses/LICENSE-2.0
109018 *
109019 * Unless required by applicable law or agreed to in writing, software
109020 * distributed under the License is distributed on an "AS IS" BASIS,
109021 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
109022 * See the License for the specific language governing permissions and
109023 * limitations under the License.
109024 */
109025
109026 namespace JMS\Serializer\Tests\Serializer;
109027
109028 use JMS\Serializer\Context;
109029 use JMS\Serializer\Metadata\ClassMetadata;
109030 use JMS\Serializer\Metadata\PropertyMetadata;
109031 use JMS\Serializer\SerializationContext;
109032 use JMS\Serializer\Tests\Fixtures\InlineChild;
109033 use JMS\Serializer\Tests\Fixtures\Node;
109034 use JMS\Serializer\SerializerBuilder;
109035
109036 class ContextTest extends \PHPUnit_Framework_TestCase
109037 {
109038 public function testSerializationContextPathAndDepth()
109039 {
109040 $object = new Node(array(
109041 new Node(),
109042 new Node(array(
109043 new Node()
109044 )),
109045 ));
109046 $objects = array($object, $object->children[0], $object->children[1], $object->children[1]->children[0]);
109047
109048 $self = $this;
109049
109050 $exclusionStrategy = $this->getMock('JMS\Serializer\Exclusion\ExclusionStrategyInterface');
109051 $exclusionStrategy->expects($this->any())
109052 ->method('shouldSkipClass')
109053 ->with($this->anything(), $this->callback(function (SerializationContext $context) use ($self, $objects) {
109054 $expectedDepth = $expectedPath = null;
109055
109056 if ($context->getObject() === $objects[0]) {
109057 $expectedDepth = 1;
109058 $expectedPath = 'JMS\Serializer\Tests\Fixtures\Node';
109059 } elseif ($context->getObject() === $objects[1]) {
109060 $expectedDepth = 2;
109061 $expectedPath = 'JMS\Serializer\Tests\Fixtures\Node -> JMS\Serializer\Tests\Fixtures\Node';
109062 } elseif ($context->getObject() === $objects[2]) {
109063 $expectedDepth = 2;
109064 $expectedPath = 'JMS\Serializer\Tests\Fixtures\Node -> JMS\Serializer\Tests\Fixtures\Node';
109065 } elseif ($context->getObject() === $objects[3]) {
109066 $expectedDepth = 3;
109067 $expectedPath = 'JMS\Serializer\Tests\Fixtures\Node -> JMS\Serializer\Tests\Fixtures\Node -> JMS\Serializer\Tests\Fixtures\Node';
109068 }
109069
109070 $self->assertEquals($expectedDepth, $context->getDepth(), 'shouldSkipClass depth');
109071 $self->assertEquals($expectedPath, $context->getPath(), 'shouldSkipClass path');
109072
109073 return true;
109074 }))
109075 ->will($this->returnValue(false));
109076
109077 $exclusionStrategy->expects($this->any())
109078 ->method('shouldSkipProperty')
109079 ->with($this->anything(), $this->callback(function (SerializationContext $context) use ($self, $objects) {
109080 $expectedDepth = $expectedPath = null;
109081
109082 if ($context->getObject() === $objects[0]) {
109083 $expectedDepth = 1;
109084 $expectedPath = 'JMS\Serializer\Tests\Fixtures\Node';
109085 } elseif ($context->getObject() === $objects[1]) {
109086 $expectedDepth = 2;
109087 $expectedPath = 'JMS\Serializer\Tests\Fixtures\Node -> JMS\Serializer\Tests\Fixtures\Node';
109088 } elseif ($context->getObject() === $objects[2]) {
109089 $expectedDepth = 2;
109090 $expectedPath = 'JMS\Serializer\Tests\Fixtures\Node -> JMS\Serializer\Tests\Fixtures\Node';
109091 } elseif ($context->getObject() === $objects[3]) {
109092 $expectedDepth = 3;
109093 $expectedPath = 'JMS\Serializer\Tests\Fixtures\Node -> JMS\Serializer\Tests\Fixtures\Node -> JMS\Serializer\Tests\Fixtures\Node';
109094 }
109095
109096 $self->assertEquals($expectedDepth, $context->getDepth(), 'shouldSkipProperty depth');
109097 $self->assertEquals($expectedPath, $context->getPath(), 'shouldSkipProperty path');
109098
109099 return true;
109100 }))
109101 ->will($this->returnValue(false));
109102
109103 $serializer = SerializerBuilder::create()->build();
109104
109105 $serializer->serialize($object, 'json', SerializationContext::create()->addExclusionStrategy($exclusionStrategy));
109106 }
109107
109108 public function testSerializationMetadataStack()
109109 {
109110 $object = new Node(array(
109111 $child = new InlineChild(),
109112 ));
109113 $self = $this;
109114
109115 $exclusionStrategy = $this->getMock('JMS\Serializer\Exclusion\ExclusionStrategyInterface');
109116 $exclusionStrategy->expects($this->any())
109117 ->method('shouldSkipClass')
109118 ->will($this->returnCallback(function (ClassMetadata $classMetadata, SerializationContext $context) use ($self, $object, $child) {
109119 $stack = $context->getMetadataStack();
109120
109121 if ($object === $context->getObject()) {
109122 $self->assertEquals(0, $stack->count());
109123 }
109124
109125 if ($child === $context->getObject()) {
109126 $self->assertEquals(2, $stack->count());
109127 $self->assertEquals('JMS\Serializer\Tests\Fixtures\Node', $stack[1]->name);
109128 $self->assertEquals('children', $stack[0]->name);
109129 }
109130
109131 return false;
109132 }));
109133
109134 $exclusionStrategy->expects($this->any())
109135 ->method('shouldSkipProperty')
109136 ->will($this->returnCallback(function (PropertyMetadata $propertyMetadata, SerializationContext $context) use ($self, $object, $child) {
109137 $stack = $context->getMetadataStack();
109138
109139 if ('JMS\Serializer\Tests\Fixtures\Node' === $propertyMetadata->class && $propertyMetadata->name === 'children') {
109140 $self->assertEquals(1, $stack->count());
109141 $self->assertEquals('JMS\Serializer\Tests\Fixtures\Node', $stack[0]->name);
109142 }
109143
109144 if ('JMS\Serializer\Tests\Fixtures\InlineChild' === $propertyMetadata->class) {
109145 $self->assertEquals(3, $stack->count());
109146 $self->assertEquals('JMS\Serializer\Tests\Fixtures\Node', $stack[2]->name);
109147 $self->assertEquals('children', $stack[1]->name);
109148 $self->assertEquals('JMS\Serializer\Tests\Fixtures\InlineChild', $stack[0]->name);
109149 }
109150
109151 return false;
109152 }));
109153
109154 $serializer = SerializerBuilder::create()->build();
109155 $serializer->serialize($object, 'json', SerializationContext::create()->addExclusionStrategy($exclusionStrategy));
109156 }
109157 }
109158 <?php
109159
109160 /*
109161 * Copyright 2013 Johannes M. Schmitt <schmittjoh@gmail.com>
109162 *
109163 * Licensed under the Apache License, Version 2.0 (the "License");
109164 * you may not use this file except in compliance with the License.
109165 * You may obtain a copy of the License at
109166 *
109167 * http://www.apache.org/licenses/LICENSE-2.0
109168 *
109169 * Unless required by applicable law or agreed to in writing, software
109170 * distributed under the License is distributed on an "AS IS" BASIS,
109171 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
109172 * See the License for the specific language governing permissions and
109173 * limitations under the License.
109174 */
109175
109176 namespace JMS\Serializer\Tests\Serializer;
109177
109178 use JMS\Serializer\Context;
109179 use JMS\Serializer\Exception\RuntimeException;
109180 use JMS\Serializer\EventDispatcher\Event;
109181 use JMS\Serializer\EventDispatcher\EventSubscriberInterface;
109182 use JMS\Serializer\GraphNavigator;
109183 use JMS\Serializer\VisitorInterface;
109184 use JMS\Serializer\Tests\Fixtures\Author;
109185 use JMS\Serializer\Tests\Fixtures\AuthorList;
109186
109187 class JsonSerializationTest extends BaseSerializationTest
109188 {
109189 protected function getContent($key)
109190 {
109191 static $outputs = array();
109192
109193 if (!$outputs) {
109194 $outputs['readonly'] = '{"id":123,"full_name":"Ruud Kamphuis"}';
109195 $outputs['string'] = '"foo"';
109196 $outputs['boolean_true'] = 'true';
109197 $outputs['boolean_false'] = 'false';
109198 $outputs['integer'] = '1';
109199 $outputs['float'] = '4.533';
109200 $outputs['float_trailing_zero'] = '1';
109201 $outputs['simple_object'] = '{"foo":"foo","moo":"bar","camel_case":"boo"}';
109202 $outputs['circular_reference'] = '{"collection":[{"name":"child1"},{"name":"child2"}],"another_collection":[{"name":"child1"},{"name":"child2"}]}';
109203 $outputs['array_strings'] = '["foo","bar"]';
109204 $outputs['array_booleans'] = '[true,false]';
109205 $outputs['array_integers'] = '[1,3,4]';
109206 $outputs['array_floats'] = '[1.34,3,6.42]';
109207 $outputs['array_objects'] = '[{"foo":"foo","moo":"bar","camel_case":"boo"},{"foo":"baz","moo":"boo","camel_case":"boo"}]';
109208 $outputs['array_mixed'] = '["foo",1,true,{"foo":"foo","moo":"bar","camel_case":"boo"},[1,3,true]]';
109209 $outputs['blog_post'] = '{"title":"This is a nice title.","created_at":"2011-07-30T00:00:00+0000","is_published":false,"comments":[{"author":{"full_name":"Foo Bar"},"text":"foo"}],"comments2":[{"author":{"full_name":"Foo Bar"},"text":"foo"}],"metadata":{"foo":"bar"},"author":{"full_name":"Foo Bar"}}';
109210 $outputs['blog_post_unauthored'] = '{"title":"This is a nice title.","created_at":"2011-07-30T00:00:00+0000","is_published":false,"comments":[],"comments2":[],"metadata":{"foo":"bar"},"author":null}';
109211 $outputs['price'] = '{"price":3}';
109212 $outputs['currency_aware_price'] = '{"currency":"EUR","amount":2.34}';
109213 $outputs['order'] = '{"cost":{"price":12.34}}';
109214 $outputs['order_with_currency_aware_price'] = '{"cost":{"currency":"EUR","amount":1.23}}';
109215 $outputs['log'] = '{"author_list":[{"full_name":"Johannes Schmitt"},{"full_name":"John Doe"}],"comments":[{"author":{"full_name":"Foo Bar"},"text":"foo"},{"author":{"full_name":"Foo Bar"},"text":"bar"},{"author":{"full_name":"Foo Bar"},"text":"baz"}]}';
109216 $outputs['lifecycle_callbacks'] = '{"name":"Foo Bar"}';
109217 $outputs['form_errors'] = '["This is the form error","Another error"]';
109218 $outputs['nested_form_errors'] = '{"errors":["This is the form error"],"children":{"bar":{"errors":["Error of the child form"]}}}';
109219 $outputs['constraint_violation'] = '{"property_path":"foo","message":"Message of violation"}';
109220 $outputs['constraint_violation_list'] = '[{"property_path":"foo","message":"Message of violation"},{"property_path":"bar","message":"Message of another violation"}]';
109221 $outputs['article'] = '{"custom":"serialized"}';
109222 $outputs['orm_proxy'] = '{"foo":"foo","moo":"bar","camel_case":"proxy-boo"}';
109223 $outputs['custom_accessor'] = '{"comments":{"Foo":{"comments":[{"author":{"full_name":"Foo"},"text":"foo"},{"author":{"full_name":"Foo"},"text":"bar"}],"count":2}}}';
109224 $outputs['mixed_access_types'] = '{"id":1,"name":"Johannes","read_only_property":42}';
109225 $outputs['accessor_order_child'] = '{"c":"c","d":"d","a":"a","b":"b"}';
109226 $outputs['accessor_order_parent'] = '{"a":"a","b":"b"}';
109227 $outputs['accessor_order_methods'] = '{"foo":"c","b":"b","a":"a"}';
109228 $outputs['inline'] = '{"c":"c","a":"a","b":"b","d":"d"}';
109229 $outputs['groups_all'] = '{"foo":"foo","foobar":"foobar","bar":"bar","none":"none"}';
109230 $outputs['groups_foo'] = '{"foo":"foo","foobar":"foobar"}';
109231 $outputs['groups_foobar'] = '{"foo":"foo","foobar":"foobar","bar":"bar"}';
109232 $outputs['groups_default'] = '{"bar":"bar","none":"none"}';
109233 $outputs['virtual_properties'] = '{"exist_field":"value","test":"other-name","virtual_value":"value","typed_virtual_property":1}';
109234 $outputs['virtual_properties_low'] = '{"low":1}';
109235 $outputs['virtual_properties_high'] = '{"high":8}';
109236 $outputs['virtual_properties_all'] = '{"low":1,"high":8}';
109237 $outputs['nullable'] = '{"foo":"bar","baz":null}';
109238 $outputs['null'] = 'null';
109239 $outputs['simple_object_nullable'] = '{"foo":"foo","moo":"bar","camel_case":"boo","null_property":null}';
109240 $outputs['input'] = '{"attributes":{"type":"text","name":"firstname","value":"Adrien"}}';
109241 $outputs['hash_empty'] = '{"hash":{}}';
109242 $outputs['object_when_null'] = '{"text":"foo"}';
109243 $outputs['object_when_null_and_serialized'] = '{"author":null,"text":"foo"}';
109244 $outputs['date_time'] = '"2011-08-30T00:00:00+0000"';
109245 $outputs['date_interval'] = '"PT45M"';
109246 $outputs['car'] = '{"km":5,"type":"car"}';
109247 $outputs['car_without_type'] = '{"km":5}';
109248 $outputs['tree'] = '{"tree":{"children":[{"children":[{"children":[],"foo":"bar"}],"foo":"bar"}],"foo":"bar"}}';
109249 }
109250
109251 if (!isset($outputs[$key])) {
109252 throw new RuntimeException(sprintf('The key "%s" is not supported.', $key));
109253 }
109254
109255 return $outputs[$key];
109256 }
109257
109258 public function testAddLinksToOutput()
109259 {
109260 $this->dispatcher->addSubscriber(new LinkAddingSubscriber());
109261 $this->handlerRegistry->registerHandler(GraphNavigator::DIRECTION_SERIALIZATION, 'JMS\Serializer\Tests\Fixtures\AuthorList', 'json',
109262 function(VisitorInterface $visitor, AuthorList $data, array $type, Context $context) {
109263 return $visitor->visitArray(iterator_to_array($data), $type, $context);
109264 }
109265 );
109266
109267 $list = new AuthorList();
109268 $list->add(new Author('foo'));
109269 $list->add(new Author('bar'));
109270
109271 $this->assertEquals('[{"full_name":"foo","_links":{"details":"http:\/\/foo.bar\/details\/foo","comments":"http:\/\/foo.bar\/details\/foo\/comments"}},{"full_name":"bar","_links":{"details":"http:\/\/foo.bar\/details\/bar","comments":"http:\/\/foo.bar\/details\/bar\/comments"}}]', $this->serialize($list));
109272 }
109273
109274 public function getPrimitiveTypes()
109275 {
109276 return array(
109277 array(
109278 'type' => 'boolean',
109279 'data' => true,
109280 ),
109281 array(
109282 'type' => 'boolean',
109283 'data' => 1,
109284 ),
109285 array(
109286 'type' => 'integer',
109287 'data' => 123,
109288 ),
109289 array(
109290 'type' => 'integer',
109291 'data' => "123",
109292 ),
109293 array(
109294 'type' => 'string',
109295 'data' => "hello",
109296 ),
109297 array(
109298 'type' => 'string',
109299 'data' => 123,
109300 ),
109301 array(
109302 'type' => 'double',
109303 'data' => 0.1234,
109304 ),
109305 array(
109306 'type' => 'double',
109307 'data' => "0.1234",
109308 ),
109309 );
109310 }
109311
109312 /**
109313 * @dataProvider getPrimitiveTypes
109314 */
109315 public function testPrimitiveTypes($primitiveType, $data)
109316 {
109317 $visitor = $this->serializationVisitors->get('json')->get();
109318 $functionToCall = 'visit' . ucfirst($primitiveType);
109319 $result = $visitor->$functionToCall($data, array(), $this->getMock('JMS\Serializer\Context'));
109320 if ($primitiveType == 'double') {
109321 $primitiveType = 'float';
109322 }
109323 $this->assertInternalType($primitiveType, $result);
109324 }
109325
109326 /**
109327 * @group empty-object
109328 */
109329 public function testSerializeEmptyObject()
109330 {
109331 $this->assertEquals('{}', $this->serialize(new Author(null)));
109332 }
109333
109334 protected function getFormat()
109335 {
109336 return 'json';
109337 }
109338 }
109339
109340 class LinkAddingSubscriber implements EventSubscriberInterface
109341 {
109342 public function onPostSerialize(Event $event)
109343 {
109344 $author = $event->getObject();
109345
109346 $event->getVisitor()->addData('_links', array(
109347 'details' => 'http://foo.bar/details/'.$author->getName(),
109348 'comments' => 'http://foo.bar/details/'.$author->getName().'/comments',
109349 ));
109350 }
109351
109352 public static function getSubscribedEvents()
109353 {
109354 return array(
109355 array('event' => 'serializer.post_serialize', 'method' => 'onPostSerialize', 'format' => 'json', 'class' => 'JMS\Serializer\Tests\Fixtures\Author'),
109356 );
109357 }
109358 }
109359 <?php
109360
109361 /*
109362 * Copyright 2013 Johannes M. Schmitt <schmittjoh@gmail.com>
109363 *
109364 * Licensed under the Apache License, Version 2.0 (the "License");
109365 * you may not use this file except in compliance with the License.
109366 * You may obtain a copy of the License at
109367 *
109368 * http://www.apache.org/licenses/LICENSE-2.0
109369 *
109370 * Unless required by applicable law or agreed to in writing, software
109371 * distributed under the License is distributed on an "AS IS" BASIS,
109372 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
109373 * See the License for the specific language governing permissions and
109374 * limitations under the License.
109375 */
109376
109377 namespace JMS\Serializer\Tests\Serializer\EventDispatcher;
109378
109379 use JMS\Serializer\EventDispatcher\Event;
109380 use JMS\Serializer\EventDispatcher\EventDispatcher;
109381 use JMS\Serializer\EventDispatcher\EventSubscriberInterface;
109382 use JMS\Serializer\EventDispatcher\ObjectEvent;
109383
109384 class EventDispatcherTest extends \PHPUnit_Framework_TestCase
109385 {
109386 private $dispatcher;
109387 private $event;
109388
109389 public function testHasListeners()
109390 {
109391 $this->assertFalse($this->dispatcher->hasListeners('foo', 'Foo', 'json'));
109392 $this->dispatcher->addListener('foo', function() { });
109393 $this->assertTrue($this->dispatcher->hasListeners('foo', 'Foo', 'json'));
109394
109395 $this->assertFalse($this->dispatcher->hasListeners('bar', 'Bar', 'json'));
109396 $this->dispatcher->addListener('bar', function() { }, 'Foo');
109397 $this->assertFalse($this->dispatcher->hasListeners('bar', 'Bar', 'json'));
109398 $this->dispatcher->addListener('bar', function() { }, 'Bar', 'xml');
109399 $this->assertFalse($this->dispatcher->hasListeners('bar', 'Bar', 'json'));
109400 $this->dispatcher->addListener('bar', function() { }, null, 'json');
109401 $this->assertTrue($this->dispatcher->hasListeners('bar', 'Baz', 'json'));
109402 $this->assertTrue($this->dispatcher->hasListeners('bar', 'Bar', 'json'));
109403
109404 $this->assertFalse($this->dispatcher->hasListeners('baz', 'Bar', 'xml'));
109405 $this->dispatcher->addListener('baz', function() { }, 'Bar');
109406 $this->assertTrue($this->dispatcher->hasListeners('baz', 'Bar', 'xml'));
109407 $this->assertTrue($this->dispatcher->hasListeners('baz', 'bAr', 'xml'));
109408 }
109409
109410 public function testDispatch()
109411 {
109412 $a = new MockListener();
109413 $this->dispatcher->addListener('foo', array($a, 'foo'));
109414 $this->dispatch('bar');
109415 $a->_verify('Listener is not called for other event.');
109416
109417 $b = new MockListener();
109418 $this->dispatcher->addListener('pre', array($b, 'bar'), 'Bar');
109419 $this->dispatcher->addListener('pre', array($b, 'foo'), 'Foo');
109420 $this->dispatcher->addListener('pre', array($b, 'all'));
109421
109422 $b->bar($this->event);
109423 $b->all($this->event);
109424 $b->foo($this->event);
109425 $b->all($this->event);
109426 $b->_replay();
109427 $this->dispatch('pre', 'Bar');
109428 $this->dispatch('pre', 'Foo');
109429 $b->_verify();
109430 }
109431
109432 public function testAddSubscriber()
109433 {
109434 $subscriber = new MockSubscriber();
109435 MockSubscriber::$events = array(
109436 array('event' => 'foo.bar_baz', 'format' => 'foo'),
109437 array('event' => 'bar', 'method' => 'bar', 'class' => 'foo'),
109438 );
109439
109440 $this->dispatcher->addSubscriber($subscriber);
109441 $this->assertAttributeEquals(array(
109442 'foo.bar_baz' => array(
109443 array(array($subscriber, 'onfoobarbaz'), null, 'foo'),
109444 ),
109445 'bar' => array(
109446 array(array($subscriber, 'bar'), 'foo', null),
109447 ),
109448 ), 'listeners', $this->dispatcher);
109449 }
109450
109451 protected function setUp()
109452 {
109453 $this->dispatcher = new EventDispatcher();
109454 $this->event = new ObjectEvent($this->getMock('JMS\Serializer\Context'), new \stdClass(), array('name' => 'foo', 'params' => array()));
109455 }
109456
109457 private function dispatch($eventName, $class = 'Foo', $format = 'json', Event $event = null)
109458 {
109459 $this->dispatcher->dispatch($eventName, $class, $format, $event ?: $this->event);
109460 }
109461 }
109462
109463 class MockSubscriber implements EventSubscriberInterface
109464 {
109465 public static $events = array();
109466
109467 public static function getSubscribedEvents()
109468 {
109469 return self::$events;
109470 }
109471 }
109472
109473 class MockListener
109474 {
109475 private $expected = array();
109476 private $actual = array();
109477 private $wasReplayed = false;
109478
109479 public function __call($method, array $args = array())
109480 {
109481 if ( ! $this->wasReplayed) {
109482 $this->expected[] = array($method, $args);
109483
109484 return;
109485 }
109486
109487 $this->actual[] = array($method, $args);
109488 }
109489
109490 public function _replay()
109491 {
109492 $this->wasReplayed = true;
109493 }
109494
109495 public function _verify($message = null)
109496 {
109497 \PHPUnit_Framework_Assert::assertSame($this->expected, $this->actual, $message);
109498 }
109499 }<?php
109500
109501 /*
109502 * Copyright 2013 Johannes M. Schmitt <schmittjoh@gmail.com>
109503 *
109504 * Licensed under the Apache License, Version 2.0 (the "License");
109505 * you may not use this file except in compliance with the License.
109506 * You may obtain a copy of the License at
109507 *
109508 * http://www.apache.org/licenses/LICENSE-2.0
109509 *
109510 * Unless required by applicable law or agreed to in writing, software
109511 * distributed under the License is distributed on an "AS IS" BASIS,
109512 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
109513 * See the License for the specific language governing permissions and
109514 * limitations under the License.
109515 */
109516
109517 namespace JMS\Serializer\Tests\Serializer\EventDispatcher\Subscriber;
109518
109519 use JMS\Serializer\Context;
109520 use JMS\Serializer\DeserializationContext;
109521 use JMS\Serializer\EventDispatcher\Event;
109522 use JMS\Serializer\EventDispatcher\EventDispatcher;
109523 use JMS\Serializer\EventDispatcher\ObjectEvent;
109524 use JMS\Serializer\EventDispatcher\Subscriber\SymfonyValidatorSubscriber;
109525 use JMS\Serializer\GraphNavigator;
109526 use JMS\Serializer\Handler\HandlerRegistryInterface;
109527 use JMS\Serializer\SerializerBuilder;
109528 use JMS\Serializer\Tests\Fixtures\AuthorList;
109529 use JMS\Serializer\Tests\Fixtures\Doctrine\Author;
109530 use Symfony\Component\Validator\ConstraintViolation;
109531 use Symfony\Component\Validator\ConstraintViolationList;
109532
109533 class SymfonyValidatorSubscriberTest extends \PHPUnit_Framework_TestCase
109534 {
109535 private $validator;
109536
109537 /** @var SymfonyValidatorSubscriber */
109538 private $subscriber;
109539
109540 /** @var Context */
109541 private $context;
109542
109543 public function testValidate()
109544 {
109545 $obj = new \stdClass;
109546
109547 $this->validator->expects($this->once())
109548 ->method('validate')
109549 ->with($obj, array('foo'))
109550 ->will($this->returnValue(new ConstraintViolationList()));
109551
109552 $context = DeserializationContext::create()->setAttribute('validation_groups', array('foo'));
109553
109554 $this->subscriber->onPostDeserialize(new ObjectEvent($context, $obj, array()));
109555 }
109556
109557 /**
109558 * @expectedException JMS\Serializer\Exception\ValidationFailedException
109559 * @expectedExceptionMessage Validation failed with 1 error(s).
109560 */
109561 public function testValidateThrowsExceptionWhenListIsNotEmpty()
109562 {
109563 $obj = new \stdClass;
109564
109565 $this->validator->expects($this->once())
109566 ->method('validate')
109567 ->with($obj, array('foo'))
109568 ->will($this->returnValue(new ConstraintViolationList(array(new ConstraintViolation('foo', array(), 'a', 'b', 'c')))));
109569
109570 $context = DeserializationContext::create()->setAttribute('validation_groups', array('foo'));
109571
109572 $this->subscriber->onPostDeserialize(new ObjectEvent($context, $obj, array()));
109573 }
109574
109575 public function testValidatorIsNotCalledWhenNoGroupsAreSet()
109576 {
109577 $this->validator->expects($this->never())
109578 ->method('validate');
109579
109580 $this->subscriber->onPostDeserialize(new ObjectEvent(DeserializationContext::create(), new \stdClass, array()));
109581 }
109582
109583 public function testValidationIsOnlyPerformedOnRootObject()
109584 {
109585 $this->validator->expects($this->once())
109586 ->method('validate')
109587 ->with($this->isInstanceOf('JMS\Serializer\Tests\Fixtures\AuthorList'), array('Foo'))
109588 ->will($this->returnValue(new ConstraintViolationList()));
109589
109590 $subscriber = $this->subscriber;
109591 $list = SerializerBuilder::create()
109592 ->configureListeners(function(EventDispatcher $dispatcher) use ($subscriber) {
109593 $dispatcher->addSubscriber($subscriber);
109594 })
109595 ->build()
109596 ->deserialize(
109597 '{"authors":[{"full_name":"foo"},{"full_name":"bar"}]}',
109598 'JMS\Serializer\Tests\Fixtures\AuthorList',
109599 'json',
109600 DeserializationContext::create()->setAttribute('validation_groups', array('Foo'))
109601 );
109602
109603 $this->assertCount(2, $list);
109604 }
109605
109606 protected function setUp()
109607 {
109608 $this->validator = $this->getMock('Symfony\Component\Validator\ValidatorInterface');
109609 $this->subscriber = new SymfonyValidatorSubscriber($this->validator);
109610 }
109611 }<?php
109612
109613 /*
109614 * Copyright 2013 Johannes M. Schmitt <schmittjoh@gmail.com>
109615 *
109616 * Licensed under the Apache License, Version 2.0 (the "License");
109617 * you may not use this file except in compliance with the License.
109618 * You may obtain a copy of the License at
109619 *
109620 * http://www.apache.org/licenses/LICENSE-2.0
109621 *
109622 * Unless required by applicable law or agreed to in writing, software
109623 * distributed under the License is distributed on an "AS IS" BASIS,
109624 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
109625 * See the License for the specific language governing permissions and
109626 * limitations under the License.
109627 */
109628
109629 namespace JMS\Serializer\Tests\Serializer\EventDispatcher\Subscriber;
109630
109631 use JMS\Serializer\EventDispatcher\PreSerializeEvent;
109632 use JMS\Serializer\EventDispatcher\Subscriber\DoctrineProxySubscriber;
109633 use JMS\Serializer\Tests\Fixtures\SimpleObjectProxy;
109634 use JMS\Serializer\VisitorInterface;
109635
109636 class DoctrineProxySubscriberTest extends \PHPUnit_Framework_TestCase
109637 {
109638 /** @var VisitorInterface */
109639 private $visitor;
109640
109641 /** @var DoctrineProxySubscriber */
109642 private $subscriber;
109643
109644 public function testRewritesProxyClassName()
109645 {
109646 $event = $this->createEvent($obj = new SimpleObjectProxy('a', 'b'), array('name' => get_class($obj), 'params' => array()));
109647 $this->subscriber->onPreSerialize($event);
109648
109649 $this->assertEquals(array('name' => get_parent_class($obj), 'params' => array()), $event->getType());
109650 $this->assertTrue($obj->__isInitialized());
109651 }
109652
109653 public function testDoesNotRewriteCustomType()
109654 {
109655 $event = $this->createEvent($obj = new SimpleObjectProxy('a', 'b'), array('name' => 'FakedName', 'params' => array()));
109656 $this->subscriber->onPreSerialize($event);
109657
109658 $this->assertEquals(array('name' => 'FakedName', 'params' => array()), $event->getType());
109659 $this->assertTrue($obj->__isInitialized());
109660 }
109661
109662 protected function setUp()
109663 {
109664 $this->subscriber = new DoctrineProxySubscriber();
109665 $this->visitor = $this->getMock('JMS\Serializer\Context');
109666 }
109667
109668 private function createEvent($object, array $type)
109669 {
109670 return new PreSerializeEvent($this->visitor, $object, $type);
109671 }
109672 }1
109673 hash: {}
109674 foo: c
109675 b: b
109676 a: a
109677 name: 'Foo Bar'
109678 - 1
109679 - 3
109680 - 4
109681 km: 5
109682 type: car
109683 currency: EUR
109684 amount: 2.34
109685 km: 5
109686 foo: foo
109687 foobar: foobar
109688 bar: bar
109689 true
109690 bar: bar
109691 none: none
109692 - foo
109693 - bar
109694 foo: bar
109695 baz: null
109696 false
109697 high: 8
109698 foo: foo
109699 moo: bar
109700 camel_case: boo
109701 id: 123
109702 full_name: 'Ruud Kamphuis'
109703 '2011-08-30T00:00:00+0000'
109704 foo: foo
109705 moo: bar
109706 camel_case: proxy-boo
109707 4.533
109708 - 1.34
109709 - 3
109710 - 6.42
109711 -
109712 foo: foo
109713 moo: bar
109714 camel_case: boo
109715 -
109716 foo: baz
109717 moo: boo
109718 camel_case: boo
109719 collection:
109720 -
109721 name: child1
109722 -
109723 name: child2
109724 another_collection:
109725 -
109726 name: child1
109727 -
109728 name: child2
109729 - true
109730 - false
109731 low: 1
109732 high: 8
109733 title: 'This is a nice title.'
109734 created_at: '2011-07-30T00:00:00+0000'
109735 is_published: false
109736 comments:
109737 -
109738 author:
109739 full_name: 'Foo Bar'
109740 text: foo
109741 comments2:
109742 -
109743 author:
109744 full_name: 'Foo Bar'
109745 text: foo
109746 metadata:
109747 foo: bar
109748 author:
109749 full_name: 'Foo Bar'
109750 author_list:
109751 -
109752 full_name: 'Johannes Schmitt'
109753 -
109754 full_name: 'John Doe'
109755 comments:
109756 -
109757 author:
109758 full_name: 'Foo Bar'
109759 text: foo
109760 -
109761 author:
109762 full_name: 'Foo Bar'
109763 text: bar
109764 -
109765 author:
109766 full_name: 'Foo Bar'
109767 text: baz
109768 cost:
109769 price: 12.34
109770 author: null
109771 text: foo
109772 custom: serialized
109773 PT45M
109774 foo
109775 title: 'This is a nice title.'
109776 created_at: '2011-07-30T00:00:00+0000'
109777 is_published: false
109778 metadata:
109779 foo: bar
109780 author: null
109781 c: c
109782 a: a
109783 b: b
109784 d: d
109785 tree:
109786 children:
109787 -
109788 children:
109789 -
109790 children:
109791 -
109792 foo: bar
109793 foo: bar
109794 foo: bar
109795 comments:
109796 Foo:
109797 comments:
109798 -
109799 author:
109800 full_name: Foo
109801 text: foo
109802 -
109803 author:
109804 full_name: Foo
109805 text: bar
109806 count: 2
109807 attributes:
109808 type: text
109809 name: firstname
109810 value: Adrien
109811 foo: foo
109812 foobar: foobar
109813 cost:
109814 currency: EUR
109815 amount: 1.23
109816 foo: foo
109817 moo: bar
109818 camel_case: boo
109819 null_property: null
109820 low: 1
109821 foo: foo
109822 foobar: foobar
109823 bar: bar
109824 none: none
109825 text: foo
109826 id: 1
109827 name: Johannes
109828 read_only_property: 42
109829 exist_field: value
109830 test: other-name
109831 virtual_value: value
109832 typed_virtual_property: 1
109833 a: a
109834 b: b
109835 null
109836 1
109837 c: c
109838 d: d
109839 a: a
109840 b: b
109841 - foo
109842 - 1
109843 - true
109844 -
109845 foo: foo
109846 moo: bar
109847 camel_case: boo
109848 -
109849 - 1
109850 - 3
109851 - true
109852 price: 3
109853 <?php
109854
109855 /*
109856 * Copyright 2013 Johannes M. Schmitt <schmittjoh@gmail.com>
109857 *
109858 * Licensed under the Apache License, Version 2.0 (the "License");
109859 * you may not use this file except in compliance with the License.
109860 * You may obtain a copy of the License at
109861 *
109862 * http://www.apache.org/licenses/LICENSE-2.0
109863 *
109864 * Unless required by applicable law or agreed to in writing, software
109865 * distributed under the License is distributed on an "AS IS" BASIS,
109866 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
109867 * See the License for the specific language governing permissions and
109868 * limitations under the License.
109869 */
109870
109871 namespace JMS\Serializer\Tests\Serializer\Naming;
109872
109873 use JMS\Serializer\Naming\IdenticalPropertyNamingStrategy;
109874
109875 class IdenticalPropertyNamingStrategyTest extends \PHPUnit_Framework_TestCase
109876 {
109877 public function providePropertyNames()
109878 {
109879 return array(
109880 array('createdAt'),
109881 array('my_field'),
109882 array('identical')
109883 );
109884 }
109885
109886 /**
109887 * @dataProvider providePropertyNames
109888 */
109889 public function testTranslateName($propertyName)
109890 {
109891 $mockProperty = $this->getMockBuilder('JMS\Serializer\Metadata\PropertyMetadata')->disableOriginalConstructor()->getMock();
109892 $mockProperty->name = $propertyName;
109893
109894 $strategy = new IdenticalPropertyNamingStrategy();
109895 $this->assertEquals($propertyName, $strategy->translateName($mockProperty));
109896 }
109897 }<?php
109898
109899 /*
109900 * Copyright 2013 Johannes M. Schmitt <schmittjoh@gmail.com>
109901 *
109902 * Licensed under the Apache License, Version 2.0 (the "License");
109903 * you may not use this file except in compliance with the License.
109904 * You may obtain a copy of the License at
109905 *
109906 * http://www.apache.org/licenses/LICENSE-2.0
109907 *
109908 * Unless required by applicable law or agreed to in writing, software
109909 * distributed under the License is distributed on an "AS IS" BASIS,
109910 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
109911 * See the License for the specific language governing permissions and
109912 * limitations under the License.
109913 */
109914
109915 namespace JMS\Serializer\Tests\Serializer;
109916
109917 use JMS\Serializer\Context;
109918 use JMS\Serializer\DeserializationContext;
109919 use JMS\Serializer\GraphNavigator;
109920 use JMS\Serializer\Handler\PhpCollectionHandler;
109921 use JMS\Serializer\SerializationContext;
109922 use JMS\Serializer\Tests\Fixtures\Discriminator\Car;
109923 use JMS\Serializer\Tests\Fixtures\Tree;
109924 use PhpCollection\Sequence;
109925 use Symfony\Component\Translation\MessageSelector;
109926 use Symfony\Component\Translation\IdentityTranslator;
109927 use JMS\Serializer\EventDispatcher\Subscriber\DoctrineProxySubscriber;
109928 use JMS\Serializer\Handler\HandlerRegistry;
109929 use JMS\Serializer\EventDispatcher\EventDispatcher;
109930 use Doctrine\Common\Annotations\AnnotationReader;
109931 use Doctrine\Common\Collections\ArrayCollection;
109932 use JMS\Serializer\Metadata\Driver\AnnotationDriver;
109933 use JMS\Serializer\Construction\UnserializeObjectConstructor;
109934 use JMS\Serializer\Handler\ArrayCollectionHandler;
109935 use JMS\Serializer\Handler\ConstraintViolationHandler;
109936 use JMS\Serializer\Handler\DateHandler;
109937 use JMS\Serializer\Handler\FormErrorHandler;
109938 use JMS\Serializer\JsonDeserializationVisitor;
109939 use JMS\Serializer\JsonSerializationVisitor;
109940 use JMS\Serializer\Naming\CamelCaseNamingStrategy;
109941 use JMS\Serializer\Naming\SerializedNameAnnotationStrategy;
109942 use JMS\Serializer\Serializer;
109943 use JMS\Serializer\VisitorInterface;
109944 use JMS\Serializer\XmlDeserializationVisitor;
109945 use JMS\Serializer\XmlSerializationVisitor;
109946 use JMS\Serializer\YamlSerializationVisitor;
109947 use JMS\Serializer\Tests\Fixtures\AccessorOrderChild;
109948 use JMS\Serializer\Tests\Fixtures\AccessorOrderParent;
109949 use JMS\Serializer\Tests\Fixtures\AccessorOrderMethod;
109950 use JMS\Serializer\Tests\Fixtures\Author;
109951 use JMS\Serializer\Tests\Fixtures\AuthorList;
109952 use JMS\Serializer\Tests\Fixtures\AuthorReadOnly;
109953 use JMS\Serializer\Tests\Fixtures\BlogPost;
109954 use JMS\Serializer\Tests\Fixtures\CircularReferenceParent;
109955 use JMS\Serializer\Tests\Fixtures\Comment;
109956 use JMS\Serializer\Tests\Fixtures\CurrencyAwareOrder;
109957 use JMS\Serializer\Tests\Fixtures\CurrencyAwarePrice;
109958 use JMS\Serializer\Tests\Fixtures\CustomDeserializationObject;
109959 use JMS\Serializer\Tests\Fixtures\GetSetObject;
109960 use JMS\Serializer\Tests\Fixtures\GroupsObject;
109961 use JMS\Serializer\Tests\Fixtures\InvalidGroupsObject;
109962 use JMS\Serializer\Tests\Fixtures\IndexedCommentsBlogPost;
109963 use JMS\Serializer\Tests\Fixtures\InlineParent;
109964 use JMS\Serializer\Tests\Fixtures\InitializedObjectConstructor;
109965 use JMS\Serializer\Tests\Fixtures\Log;
109966 use JMS\Serializer\Tests\Fixtures\ObjectWithLifecycleCallbacks;
109967 use JMS\Serializer\Tests\Fixtures\ObjectWithVersionedVirtualProperties;
109968 use JMS\Serializer\Tests\Fixtures\ObjectWithVirtualProperties;
109969 use JMS\Serializer\Tests\Fixtures\Order;
109970 use JMS\Serializer\Tests\Fixtures\Price;
109971 use JMS\Serializer\Tests\Fixtures\SimpleObject;
109972 use JMS\Serializer\Tests\Fixtures\ObjectWithNullProperty;
109973 use JMS\Serializer\Tests\Fixtures\SimpleObjectProxy;
109974 use JMS\Serializer\Tests\Fixtures\Article;
109975 use JMS\Serializer\Tests\Fixtures\Input;
109976 use JMS\Serializer\Tests\Fixtures\ObjectWithEmptyHash;
109977 use Metadata\MetadataFactory;
109978 use Symfony\Component\Form\Form;
109979 use Symfony\Component\Form\FormError;
109980 use Symfony\Component\Validator\ConstraintViolation;
109981 use Symfony\Component\Validator\ConstraintViolationList;
109982 use PhpCollection\Map;
109983 use JMS\Serializer\Exclusion\DepthExclusionStrategy;
109984 use JMS\Serializer\Tests\Fixtures\Node;
109985
109986 abstract class BaseSerializationTest extends \PHPUnit_Framework_TestCase
109987 {
109988 protected $factory;
109989 protected $dispatcher;
109990
109991 /** @var Serializer */
109992 protected $serializer;
109993 protected $handlerRegistry;
109994 protected $serializationVisitors;
109995 protected $deserializationVisitors;
109996
109997 public function testSerializeNullArray()
109998 {
109999 $arr = array('foo' => 'bar', 'baz' => null, null);
110000
110001 $this->assertEquals(
110002 $this->getContent('nullable'),
110003 $this->serializer->serialize($arr, $this->getFormat(), SerializationContext::create()->setSerializeNull(true))
110004 );
110005 }
110006
110007 public function testSerializeNullObject()
110008 {
110009 $obj = new ObjectWithNullProperty('foo', 'bar');
110010
110011 $this->assertEquals(
110012 $this->getContent('simple_object_nullable'),
110013 $this->serializer->serialize($obj, $this->getFormat(), SerializationContext::create()->setSerializeNull(true))
110014 );
110015 }
110016
110017 /**
110018 * @dataProvider getTypes
110019 */
110020 public function testNull($type)
110021 {
110022 $this->assertEquals($this->getContent('null'), $this->serialize(null), $type);
110023
110024 if ($this->hasDeserializer()) {
110025 $this->assertEquals(null, $this->deserialize($this->getContent('null'), $type));
110026 }
110027 }
110028
110029 public function getTypes()
110030 {
110031 return array(
110032 array('NULL'),
110033 array('integer'),
110034 array('double'),
110035 array('float'),
110036 array('string'),
110037 array('DateTime'),
110038 );
110039 }
110040
110041 public function testString()
110042 {
110043 $this->assertEquals($this->getContent('string'), $this->serialize('foo'));
110044
110045 if ($this->hasDeserializer()) {
110046 $this->assertEquals('foo', $this->deserialize($this->getContent('string'), 'string'));
110047 }
110048 }
110049
110050 /**
110051 * @dataProvider getBooleans
110052 */
110053 public function testBooleans($strBoolean, $boolean)
110054 {
110055 $this->assertEquals($this->getContent('boolean_'.$strBoolean), $this->serialize($boolean));
110056
110057 if ($this->hasDeserializer()) {
110058 $this->assertSame($boolean, $this->deserialize($this->getContent('boolean_'.$strBoolean), 'boolean'));
110059 }
110060 }
110061
110062 public function getBooleans()
110063 {
110064 return array(array('true', true), array('false', false));
110065 }
110066
110067 /**
110068 * @dataProvider getNumerics
110069 */
110070 public function testNumerics($key, $value, $type)
110071 {
110072 $this->assertEquals($this->getContent($key), $this->serialize($value));
110073
110074 if ($this->hasDeserializer()) {
110075 $this->assertEquals($value, $this->deserialize($this->getContent($key), $type));
110076 }
110077 }
110078
110079 public function getNumerics()
110080 {
110081 return array(
110082 array('integer', 1, 'integer'),
110083 array('float', 4.533, 'double'),
110084 array('float', 4.533, 'float'),
110085 array('float_trailing_zero', 1.0, 'double'),
110086 array('float_trailing_zero', 1.0, 'float'),
110087 );
110088 }
110089
110090 public function testSimpleObject()
110091 {
110092 $this->assertEquals($this->getContent('simple_object'), $this->serialize($obj = new SimpleObject('foo', 'bar')));
110093
110094 if ($this->hasDeserializer()) {
110095 $this->assertEquals($obj, $this->deserialize($this->getContent('simple_object'), get_class($obj)));
110096 }
110097 }
110098
110099 public function testArrayStrings()
110100 {
110101 $data = array('foo', 'bar');
110102 $this->assertEquals($this->getContent('array_strings'), $this->serialize($data));
110103
110104 if ($this->hasDeserializer()) {
110105 $this->assertEquals($data, $this->deserialize($this->getContent('array_strings'), 'array<string>'));
110106 }
110107 }
110108
110109 public function testArrayBooleans()
110110 {
110111 $data = array(true, false);
110112 $this->assertEquals($this->getContent('array_booleans'), $this->serialize($data));
110113
110114 if ($this->hasDeserializer()) {
110115 $this->assertEquals($data, $this->deserialize($this->getContent('array_booleans'), 'array<boolean>'));
110116 }
110117 }
110118
110119 public function testArrayIntegers()
110120 {
110121 $data = array(1, 3, 4);
110122 $this->assertEquals($this->getContent('array_integers'), $this->serialize($data));
110123
110124 if ($this->hasDeserializer()) {
110125 $this->assertEquals($data, $this->deserialize($this->getContent('array_integers'), 'array<integer>'));
110126 }
110127 }
110128
110129 public function testArrayFloats()
110130 {
110131 $data = array(1.34, 3.0, 6.42);
110132 $this->assertEquals($this->getContent('array_floats'), $this->serialize($data));
110133
110134 if ($this->hasDeserializer()) {
110135 $this->assertEquals($data, $this->deserialize($this->getContent('array_floats'), 'array<double>'));
110136 }
110137 }
110138
110139 public function testArrayObjects()
110140 {
110141 $data = array(new SimpleObject('foo', 'bar'), new SimpleObject('baz', 'boo'));
110142 $this->assertEquals($this->getContent('array_objects'), $this->serialize($data));
110143
110144 if ($this->hasDeserializer()) {
110145 $this->assertEquals($data, $this->deserialize($this->getContent('array_objects'), 'array<JMS\Serializer\Tests\Fixtures\SimpleObject>'));
110146 }
110147 }
110148
110149 public function testArrayMixed()
110150 {
110151 $this->assertEquals($this->getContent('array_mixed'), $this->serialize(array('foo', 1, true, new SimpleObject('foo', 'bar'), array(1, 3, true))));
110152 }
110153
110154 /**
110155 * @dataProvider getDateTime
110156 * @group datetime
110157 */
110158 public function testDateTime($key, $value, $type)
110159 {
110160 $this->assertEquals($this->getContent($key), $this->serialize($value));
110161
110162 if ($this->hasDeserializer()) {
110163 $deserialized = $this->deserialize($this->getContent($key), $type);
110164
110165 $this->assertTrue(is_object($deserialized));
110166 $this->assertEquals(get_class($value), get_class($deserialized));
110167 $this->assertEquals($value->getTimestamp(), $deserialized->getTimestamp());
110168 }
110169 }
110170
110171 public function getDateTime()
110172 {
110173 return array(
110174 array('date_time', new \DateTime('2011-08-30 00:00', new \DateTimeZone('UTC')), 'DateTime'),
110175 );
110176 }
110177
110178 public function testDateInterval()
110179 {
110180 $duration = new \DateInterval('PT45M');
110181
110182 $this->assertEquals($this->getContent('date_interval'), $this->serializer->serialize($duration, $this->getFormat()));
110183 }
110184
110185 public function testBlogPost()
110186 {
110187 $post = new BlogPost('This is a nice title.', $author = new Author('Foo Bar'), new \DateTime('2011-07-30 00:00', new \DateTimeZone('UTC')));
110188 $post->addComment($comment = new Comment($author, 'foo'));
110189
110190 $this->assertEquals($this->getContent('blog_post'), $this->serialize($post));
110191
110192 if ($this->hasDeserializer()) {
110193 $deserialized = $this->deserialize($this->getContent('blog_post'), get_class($post));
110194 $this->assertEquals('2011-07-30T00:00:00+0000', $this->getField($deserialized, 'createdAt')->format(\DateTime::ISO8601));
110195 $this->assertAttributeEquals('This is a nice title.', 'title', $deserialized);
110196 $this->assertAttributeSame(false, 'published', $deserialized);
110197 $this->assertAttributeEquals(new ArrayCollection(array($comment)), 'comments', $deserialized);
110198 $this->assertAttributeEquals(new Sequence(array($comment)), 'comments2', $deserialized);
110199 $this->assertAttributeEquals($author, 'author', $deserialized);
110200 }
110201 }
110202
110203 public function testDeserializingNull()
110204 {
110205 $objectConstructor = new InitializedObjectConstructor();
110206 $this->serializer = new Serializer($this->factory, $this->handlerRegistry, $objectConstructor, $this->serializationVisitors, $this->deserializationVisitors, $this->dispatcher);
110207
110208 $post = new BlogPost('This is a nice title.', $author = new Author('Foo Bar'), new \DateTime('2011-07-30 00:00', new \DateTimeZone('UTC')));
110209
110210 $this->setField($post, 'author', null);
110211
110212 $this->assertEquals($this->getContent('blog_post_unauthored'), $this->serialize($post, SerializationContext::create()->setSerializeNull(true)));
110213
110214 if ($this->hasDeserializer()) {
110215 $deserialized = $this->deserialize($this->getContent('blog_post_unauthored'), get_class($post), DeserializationContext::create()->setSerializeNull(true));
110216
110217 $this->assertEquals('2011-07-30T00:00:00+0000', $this->getField($deserialized, 'createdAt')->format(\DateTime::ISO8601));
110218 $this->assertAttributeEquals('This is a nice title.', 'title', $deserialized);
110219 $this->assertAttributeSame(false, 'published', $deserialized);
110220 $this->assertAttributeEquals(new ArrayCollection(), 'comments', $deserialized);
110221 $this->assertEquals(null, $this->getField($deserialized, 'author'));
110222 }
110223 }
110224
110225 public function testReadOnly()
110226 {
110227 $author = new AuthorReadOnly(123, 'Ruud Kamphuis');
110228 $this->assertEquals($this->getContent('readonly'), $this->serialize($author));
110229
110230 if ($this->hasDeserializer()) {
110231 $deserialized = $this->deserialize($this->getContent('readonly'), get_class($author));
110232 $this->assertNull($this->getField($deserialized, 'id'));
110233 $this->assertEquals('Ruud Kamphuis', $this->getField($deserialized, 'name'));
110234 }
110235 }
110236
110237 public function testPrice()
110238 {
110239 $price = new Price(3);
110240 $this->assertEquals($this->getContent('price'), $this->serialize($price));
110241
110242 if ($this->hasDeserializer()) {
110243 $deserialized = $this->deserialize($this->getContent('price'), get_class($price));
110244 $this->assertEquals(3, $this->getField($deserialized, 'price'));
110245 }
110246 }
110247
110248 public function testOrder()
110249 {
110250 $order = new Order(new Price(12.34));
110251 $this->assertEquals($this->getContent('order'), $this->serialize($order));
110252
110253 if ($this->hasDeserializer()) {
110254 $this->assertEquals($order, $this->deserialize($this->getContent('order'), get_class($order)));
110255 }
110256 }
110257
110258 public function testCurrencyAwarePrice()
110259 {
110260 $price = new CurrencyAwarePrice(2.34);
110261 $this->assertEquals($this->getContent('currency_aware_price'), $this->serialize($price));
110262
110263 if ($this->hasDeserializer()) {
110264 $this->assertEquals($price, $this->deserialize($this->getContent('currency_aware_price'), get_class($price)));
110265 }
110266 }
110267
110268 public function testOrderWithCurrencyAwarePrice()
110269 {
110270 $order = new CurrencyAwareOrder(new CurrencyAwarePrice(1.23));
110271 $this->assertEquals($this->getContent('order_with_currency_aware_price'), $this->serialize($order));
110272
110273 if ($this->hasDeserializer()) {
110274 $this->assertEquals($order, $this->deserialize($this->getContent('order_with_currency_aware_price'), get_class($order)));
110275 }
110276 }
110277
110278 /**
110279 * @group handlerCallback
110280 */
110281 public function testArticle()
110282 {
110283 $article = new Article();
110284 $article->element = 'custom';
110285 $article->value = 'serialized';
110286
110287 $result = $this->serialize($article);
110288 $this->assertEquals($this->getContent('article'), $result);
110289
110290 if ($this->hasDeserializer()) {
110291 $this->assertEquals($article, $this->deserialize($result, 'JMS\Serializer\Tests\Fixtures\Article'));
110292 }
110293 }
110294
110295 public function testInline()
110296 {
110297 $inline = new InlineParent();
110298
110299 $result = $this->serialize($inline);
110300 $this->assertEquals($this->getContent('inline'), $result);
110301
110302 // no deserialization support
110303 }
110304
110305 /**
110306 * @group log
110307 */
110308 public function testLog()
110309 {
110310 $this->assertEquals($this->getContent('log'), $this->serialize($log = new Log()));
110311
110312 if ($this->hasDeserializer()) {
110313 $deserialized = $this->deserialize($this->getContent('log'), get_class($log));
110314 $this->assertEquals($log, $deserialized);
110315 }
110316 }
110317
110318 public function testCircularReference()
110319 {
110320 $object = new CircularReferenceParent();
110321 $this->assertEquals($this->getContent('circular_reference'), $this->serialize($object));
110322
110323 if ($this->hasDeserializer()) {
110324 $deserialized = $this->deserialize($this->getContent('circular_reference'), get_class($object));
110325
110326 $col = $this->getField($deserialized, 'collection');
110327 $this->assertEquals(2, count($col));
110328 $this->assertEquals('child1', $col[0]->getName());
110329 $this->assertEquals('child2', $col[1]->getName());
110330 $this->assertSame($deserialized, $col[0]->getParent());
110331 $this->assertSame($deserialized, $col[1]->getParent());
110332
110333 $col = $this->getField($deserialized, 'anotherCollection');
110334 $this->assertEquals(2, count($col));
110335 $this->assertEquals('child1', $col[0]->getName());
110336 $this->assertEquals('child2', $col[1]->getName());
110337 $this->assertSame($deserialized, $col[0]->getParent());
110338 $this->assertSame($deserialized, $col[1]->getParent());
110339 }
110340 }
110341
110342 public function testLifecycleCallbacks()
110343 {
110344 $object = new ObjectWithLifecycleCallbacks();
110345 $this->assertEquals($this->getContent('lifecycle_callbacks'), $this->serialize($object));
110346 $this->assertAttributeSame(null, 'name', $object);
110347
110348 if ($this->hasDeserializer()) {
110349 $deserialized = $this->deserialize($this->getContent('lifecycle_callbacks'), get_class($object));
110350 $this->assertEquals($object, $deserialized);
110351 }
110352 }
110353
110354 public function testFormErrors()
110355 {
110356 $errors = array(
110357 new FormError('This is the form error'),
110358 new FormError('Another error')
110359 );
110360
110361 $this->assertEquals($this->getContent('form_errors'), $this->serialize($errors));
110362 }
110363
110364 public function testNestedFormErrors()
110365 {
110366 $dispatcher = $this->getMock('Symfony\Component\EventDispatcher\EventDispatcherInterface');
110367
110368 $formConfigBuilder = new \Symfony\Component\Form\FormConfigBuilder('foo', null, $dispatcher);
110369 $formConfigBuilder->setCompound(true);
110370 $formConfigBuilder->setDataMapper($this->getMock('Symfony\Component\Form\DataMapperInterface'));
110371 $fooConfig = $formConfigBuilder->getFormConfig();
110372
110373 $form = new Form($fooConfig);
110374 $form->addError(new FormError('This is the form error'));
110375
110376 $formConfigBuilder = new \Symfony\Component\Form\FormConfigBuilder('bar', null, $dispatcher);
110377 $barConfig = $formConfigBuilder->getFormConfig();
110378 $child = new Form($barConfig);
110379 $child->addError(new FormError('Error of the child form'));
110380 $form->add($child);
110381
110382 $this->assertEquals($this->getContent('nested_form_errors'), $this->serialize($form));
110383 }
110384
110385 public function testConstraintViolation()
110386 {
110387 $violation = new ConstraintViolation('Message of violation', array(), null, 'foo', null);
110388
110389 $this->assertEquals($this->getContent('constraint_violation'), $this->serialize($violation));
110390 }
110391
110392 public function testConstraintViolationList()
110393 {
110394 $violations = new ConstraintViolationList();
110395 $violations->add(new ConstraintViolation('Message of violation', array(), null, 'foo', null));
110396 $violations->add(new ConstraintViolation('Message of another violation', array(), null, 'bar', null));
110397
110398 $this->assertEquals($this->getContent('constraint_violation_list'), $this->serialize($violations));
110399 }
110400
110401 public function testDoctrineProxy()
110402 {
110403 if (!class_exists('Doctrine\ORM\Version')) {
110404 $this->markTestSkipped('Doctrine is not available.');
110405 }
110406
110407 $object = new SimpleObjectProxy('foo', 'bar');
110408
110409 $this->assertEquals($this->getContent('orm_proxy'), $this->serialize($object));
110410 }
110411
110412 public function testInitializedDoctrineProxy()
110413 {
110414 if (!class_exists('Doctrine\ORM\Version')) {
110415 $this->markTestSkipped('Doctrine is not available.');
110416 }
110417
110418 $object = new SimpleObjectProxy('foo', 'bar');
110419 $object->__load();
110420
110421 $this->assertEquals($this->getContent('orm_proxy'), $this->serialize($object));
110422 }
110423
110424 public function testCustomAccessor()
110425 {
110426 $post = new IndexedCommentsBlogPost();
110427
110428 $this->assertEquals($this->getContent('custom_accessor'), $this->serialize($post));
110429 }
110430
110431 public function testMixedAccessTypes()
110432 {
110433 $object = new GetSetObject();
110434
110435 $this->assertEquals($this->getContent('mixed_access_types'), $this->serialize($object));
110436
110437 if ($this->hasDeserializer()) {
110438 $object = $this->deserialize($this->getContent('mixed_access_types'), 'JMS\Serializer\Tests\Fixtures\GetSetObject');
110439 $this->assertAttributeEquals(1, 'id', $object);
110440 $this->assertAttributeEquals('Johannes', 'name', $object);
110441 $this->assertAttributeEquals(42, 'readOnlyProperty', $object);
110442 }
110443 }
110444
110445 public function testAccessorOrder()
110446 {
110447 $this->assertEquals($this->getContent('accessor_order_child'), $this->serialize(new AccessorOrderChild()));
110448 $this->assertEquals($this->getContent('accessor_order_parent'), $this->serialize(new AccessorOrderParent()));
110449 $this->assertEquals($this->getContent('accessor_order_methods'), $this->serialize(new AccessorOrderMethod()));
110450 }
110451
110452 public function testGroups()
110453 {
110454 $groupsObject = new GroupsObject();
110455
110456 $this->assertEquals($this->getContent('groups_all'), $this->serializer->serialize($groupsObject, $this->getFormat()));
110457
110458 $this->assertEquals(
110459 $this->getContent('groups_foo'),
110460 $this->serializer->serialize($groupsObject, $this->getFormat(), SerializationContext::create()->setGroups(array('foo')))
110461 );
110462
110463 $this->assertEquals(
110464 $this->getContent('groups_foobar'),
110465 $this->serializer->serialize($groupsObject, $this->getFormat(), SerializationContext::create()->setGroups(array('foo', 'bar')))
110466 );
110467
110468 $this->assertEquals(
110469 $this->getContent('groups_all'),
110470 $this->serializer->serialize($groupsObject, $this->getFormat())
110471 );
110472
110473 $this->assertEquals(
110474 $this->getContent('groups_default'),
110475 $this->serializer->serialize($groupsObject, $this->getFormat(), SerializationContext::create()->setGroups(array('Default')))
110476 );
110477 }
110478
110479 /**
110480 * @expectedException JMS\Serializer\Exception\InvalidArgumentException
110481 * @expectedExceptionMessage Invalid group name "foo, bar" on "JMS\Serializer\Tests\Fixtures\InvalidGroupsObject->foo", did you mean to create multiple groups?
110482 */
110483 public function testInvalidGroupName()
110484 {
110485 $groupsObject = new InvalidGroupsObject();
110486
110487 $this->serializer->serialize($groupsObject, $this->getFormat());
110488 }
110489
110490 public function testVirtualProperty()
110491 {
110492 $this->assertEquals($this->getContent('virtual_properties'), $this->serialize(new ObjectWithVirtualProperties()));
110493 }
110494
110495 public function testVirtualVersions()
110496 {
110497 $this->assertEquals(
110498 $this->getContent('virtual_properties_low'),
110499 $this->serialize(new ObjectWithVersionedVirtualProperties(), SerializationContext::create()->setVersion(2))
110500 );
110501
110502 $this->assertEquals(
110503 $this->getContent('virtual_properties_all'),
110504 $this->serialize(new ObjectWithVersionedVirtualProperties(), SerializationContext::create()->setVersion(7))
110505 );
110506
110507 $this->assertEquals(
110508 $this->getContent('virtual_properties_high'),
110509 $this->serialize(new ObjectWithVersionedVirtualProperties(), SerializationContext::create()->setVersion(9))
110510 );
110511 }
110512
110513 public function testCustomHandler()
110514 {
110515 if ( ! $this->hasDeserializer()) {
110516 return;
110517 }
110518
110519 $handler = function() {
110520 return new CustomDeserializationObject('customly_unserialized_value');
110521 };
110522
110523 $this->handlerRegistry->registerHandler(GraphNavigator::DIRECTION_DESERIALIZATION, 'CustomDeserializationObject', $this->getFormat(), $handler);
110524
110525 $serialized = $this->serializer->serialize(new CustomDeserializationObject('sometext'), $this->getFormat());
110526 $object = $this->serializer->deserialize($serialized, 'CustomDeserializationObject', $this->getFormat());
110527 $this->assertEquals('customly_unserialized_value', $object->someProperty);
110528 }
110529
110530 public function testInput()
110531 {
110532 $this->assertEquals($this->getContent('input'), $this->serializer->serialize(new Input(), $this->getFormat()));
110533 }
110534
110535 public function testObjectWithEmptyHash()
110536 {
110537 $this->assertEquals($this->getContent('hash_empty'), $this->serializer->serialize(new ObjectWithEmptyHash(), $this->getFormat()));
110538 }
110539
110540 /**
110541 * @group null
110542 */
110543 public function testSerializeObjectWhenNull()
110544 {
110545 $this->assertEquals(
110546 $this->getContent('object_when_null'),
110547 $this->serialize(new Comment(null, 'foo'), SerializationContext::create()->setSerializeNull(false))
110548 );
110549
110550 $this->assertEquals(
110551 $this->getContent('object_when_null_and_serialized'),
110552 $this->serialize(new Comment(null, 'foo'), SerializationContext::create()->setSerializeNull(true))
110553 );
110554 }
110555
110556 /**
110557 * @group polymorphic
110558 */
110559 public function testPolymorphicObjects()
110560 {
110561 $this->assertEquals(
110562 $this->getContent('car'),
110563 $this->serialize(new Car(5))
110564 );
110565
110566 if ($this->hasDeserializer()) {
110567 $this->assertEquals(
110568 new Car(5),
110569 $this->deserialize(
110570 $this->getContent('car'),
110571 'JMS\Serializer\Tests\Fixtures\Discriminator\Car'
110572 ),
110573 'Class is resolved correctly when concrete sub-class is used.'
110574 );
110575
110576 $this->assertEquals(
110577 new Car(5),
110578 $this->deserialize(
110579 $this->getContent('car'),
110580 'JMS\Serializer\Tests\Fixtures\Discriminator\Vehicle'
110581 ),
110582 'Class is resolved correctly when least supertype is used.'
110583 );
110584
110585 $this->assertEquals(
110586 new Car(5),
110587 $this->deserialize(
110588 $this->getContent('car_without_type'),
110589 'JMS\Serializer\Tests\Fixtures\Discriminator\Car'
110590 ),
110591 'Class is resolved correctly when concrete sub-class is used and no type is defined.'
110592 );
110593 }
110594 }
110595
110596 /**
110597 * @group polymorphic
110598 * @expectedException LogicException
110599 */
110600 public function testPolymorphicObjectsInvalidDeserialization()
110601 {
110602 if (!$this->hasDeserializer()) {
110603 throw new \LogicException('No deserializer');
110604 }
110605
110606 $this->deserialize(
110607 $this->getContent('car_without_type'),
110608 'JMS\Serializer\Tests\Fixtures\Discriminator\Vehicle'
110609 );
110610 }
110611
110612 public function testDepthExclusionStrategy()
110613 {
110614 $context = SerializationContext::create()
110615 ->addExclusionStrategy(new DepthExclusionStrategy())
110616 ;
110617
110618 $data = new Tree(
110619 new Node(array(
110620 new Node(array(
110621 new Node(array(
110622 new Node(array(
110623 new Node(),
110624 )),
110625 )),
110626 )),
110627 ))
110628 );
110629
110630 $this->assertEquals($this->getContent('tree'), $this->serializer->serialize($data, $this->getFormat(), $context));
110631 }
110632
110633 abstract protected function getContent($key);
110634 abstract protected function getFormat();
110635
110636 protected function hasDeserializer()
110637 {
110638 return true;
110639 }
110640
110641 protected function serialize($data, Context $context = null)
110642 {
110643 return $this->serializer->serialize($data, $this->getFormat(), $context);
110644 }
110645
110646 protected function deserialize($content, $type, Context $context = null)
110647 {
110648 return $this->serializer->deserialize($content, $type, $this->getFormat(), $context);
110649 }
110650
110651 protected function setUp()
110652 {
110653 $this->factory = new MetadataFactory(new AnnotationDriver(new AnnotationReader()));
110654
110655 $this->handlerRegistry = new HandlerRegistry();
110656 $this->handlerRegistry->registerSubscribingHandler(new ConstraintViolationHandler());
110657 $this->handlerRegistry->registerSubscribingHandler(new DateHandler());
110658 $this->handlerRegistry->registerSubscribingHandler(new FormErrorHandler(new IdentityTranslator(new MessageSelector())));
110659 $this->handlerRegistry->registerSubscribingHandler(new PhpCollectionHandler());
110660 $this->handlerRegistry->registerSubscribingHandler(new ArrayCollectionHandler());
110661 $this->handlerRegistry->registerHandler(GraphNavigator::DIRECTION_SERIALIZATION, 'AuthorList', $this->getFormat(),
110662 function(VisitorInterface $visitor, $object, array $type, Context $context) {
110663 return $visitor->visitArray(iterator_to_array($object), $type, $context);
110664 }
110665 );
110666 $this->handlerRegistry->registerHandler(GraphNavigator::DIRECTION_DESERIALIZATION, 'AuthorList', $this->getFormat(),
110667 function(VisitorInterface $visitor, $data, $type, Context $context) {
110668 $type = array(
110669 'name' => 'array',
110670 'params' => array(
110671 array('name' => 'integer', 'params' => array()),
110672 array('name' => 'JMS\Serializer\Tests\Fixtures\Author', 'params' => array()),
110673 ),
110674 );
110675
110676 $elements = $visitor->getNavigator()->accept($data, $type, $context);
110677 $list = new AuthorList();
110678 foreach ($elements as $author) {
110679 $list->add($author);
110680 }
110681
110682 return $list;
110683 }
110684 );
110685
110686 $this->dispatcher = new EventDispatcher();
110687 $this->dispatcher->addSubscriber(new DoctrineProxySubscriber());
110688
110689 $namingStrategy = new SerializedNameAnnotationStrategy(new CamelCaseNamingStrategy());
110690 $objectConstructor = new UnserializeObjectConstructor();
110691 $this->serializationVisitors = new Map(array(
110692 'json' => new JsonSerializationVisitor($namingStrategy),
110693 'xml' => new XmlSerializationVisitor($namingStrategy),
110694 'yml' => new YamlSerializationVisitor($namingStrategy),
110695 ));
110696 $this->deserializationVisitors = new Map(array(
110697 'json' => new JsonDeserializationVisitor($namingStrategy),
110698 'xml' => new XmlDeserializationVisitor($namingStrategy),
110699 ));
110700
110701 $this->serializer = new Serializer($this->factory, $this->handlerRegistry, $objectConstructor, $this->serializationVisitors, $this->deserializationVisitors, $this->dispatcher);
110702 }
110703
110704 private function getField($obj, $name)
110705 {
110706 $ref = new \ReflectionProperty($obj, $name);
110707 $ref->setAccessible(true);
110708
110709 return $ref->getValue($obj);
110710 }
110711
110712 private function setField($obj, $name, $value)
110713 {
110714 $ref = new \ReflectionProperty($obj, $name);
110715 $ref->setAccessible(true);
110716 $ref->setValue($obj, $value);
110717 }
110718 }
110719 <?xml version="1.0" encoding="UTF-8"?>
110720 <serializer>
110721 <class name="JMS\Serializer\Tests\Fixtures\Discriminator\Vehicle" discriminator-field-name="type">
110722 <discriminator-class value="car">JMS\Serializer\Tests\Fixtures\Discriminator\Car</discriminator-class>
110723 <discriminator-class value="moped">JMS\Serializer\Tests\Fixtures\Discriminator\Moped</discriminator-class>
110724 <property name="km" type="integer" />
110725 </class>
110726 </serializer>This contains no valid XML markup.<?xml version="1.0" encoding="UTF-8"?>
110727 <serializer>
110728 <class name="JMS\Serializer\Tests\Fixtures\Price">
110729 <property name="price" xml-value="true" type="double" />
110730 </class>
110731 </serializer><?xml version="1.0" encoding="UTF-8"?>
110732 <serializer>
110733 <class name="JMS\Serializer\Tests\Fixtures\Discriminator\Car">
110734 </class>
110735 </serializer><?xml version="1.0" encoding="UTF-8"?>
110736 <serializer>
110737 <class name="JMS\Serializer\Tests\Fixtures\ObjectWithVirtualProperties">
110738 <property name="existField" type="string"/>
110739 <virtual-property method="getVirtualValue"/>
110740 <virtual-property method="getVirtualSerializedValue" serialized-name="test"/>
110741 <virtual-property method="getTypedVirtualProperty" type="integer" />
110742 </class>
110743 </serializer>
110744 <?xml version="1.0" encoding="UTF-8"?>
110745 <serializer>
110746 <class name="JMS\Serializer\Tests\Fixtures\GetSetObject" access-type="public_method">
110747 <property name="id" type="integer" access-type="property" />
110748 <property name="name" type="string" accessor-getter="getTrimmedName" accessor-setter="setCapitalizedName" />
110749 </class>
110750 </serializer><?xml version="1.0" encoding="UTF-8"?>
110751 <serializer>
110752 <class name="JMS\Serializer\Tests\Fixtures\AuthorReadOnly" xml-root-name="author">
110753 <property name="id" read-only="true"/>
110754 <property name="name" serialized-name="full_name" access-type="public_method" accessor-getter="getName" read-only="true"/>
110755 </class>
110756 </serializer><?xml version="1.0" encoding="UTF-8"?>
110757 <serializer>
110758 <class name="JMS\Serializer\Tests\Fixtures\BlogPost" xml-root-name="blog-post">
110759 <property name="title" type="string" groups="comments,post"/>
110760 <property name="createdAt" xml-attribute="true" type="DateTime"/>
110761 <property name="published" type="boolean" serialized-name="is_published" xml-attribute="true" groups="post" />
110762 <property name="comments" groups="comments">
110763 <type><![CDATA[ArrayCollection<JMS\Serializer\Tests\Fixtures\Comment>]]></type>
110764 <xml-list inline="true" entry-name="comment" />
110765 </property>
110766 <property name="author" groups="post" type="JMS\Serializer\Tests\Fixtures\Author" />
110767 </class>
110768 </serializer><?xml version="1.0" encoding="UTF-8"?>
110769 <serializer>
110770 <class name="JMS\Serializer\Tests\Fixtures\BlogPost" xml-root-name="blog-post" exclusion-policy="ALL">
110771 <property name="title" type="string" expose="true" />
110772 </class>
110773 </serializer><?xml version="1.0" encoding="UTF-8"?>
110774 <serializer>
110775 <class name="JMS\Serializer\Tests\Fixtures\ObjectWithVirtualPropertiesAndExcludeAll" exclusion-policy="ALL">
110776 <virtual-property method="getVirtualValue"/>
110777 </class>
110778 </serializer><?xml version="1.0" encoding="UTF-8"?>
110779 <serializer>
110780 <class name="JMS\Serializer\Tests\Fixtures\Discriminator\Moped">
110781 </class>
110782 </serializer><?xml version="1.0" encoding="UTF-8"?>
110783 <serializer>
110784 <class name="JMS\Serializer\Tests\Fixtures\BlogPost" xml-root-name="blog-post" exclusion-policy="NONE">
110785 <property name="title" type="string" exclude="true" />
110786 </class>
110787 </serializer><?xml version="1.0" encoding="UTF-8"?>
110788 <serializer>
110789 <class name="JMS\Serializer\Tests\Fixtures\ObjectWithXmlKeyValuePairs" >
110790 <property name="array" type="array" xml-key-value-pairs="true" />
110791 </class>
110792 </serializer>
110793 <?xml version="1.0" encoding="UTF-8"?>
110794 <serializer>
110795 <class name="JMS\Serializer\Tests\Fixtures\Node">
110796 <property name="children" max-depth="2" />
110797 </class>
110798 </serializer>
110799 <?xml version="1.0" encoding="UTF-8"?>
110800 <serializer>
110801 <class name="JMS\Serializer\Tests\Fixtures\BlogPost" xml-root-name="blog-post" exclusion-policy="all">
110802 <property name="title" type="string" expose="TRUE" />
110803 </class>
110804 </serializer><?php
110805
110806 /*
110807 * Copyright 2013 Johannes M. Schmitt <schmittjoh@gmail.com>
110808 *
110809 * Licensed under the Apache License, Version 2.0 (the "License");
110810 * you may not use this file except in compliance with the License.
110811 * You may obtain a copy of the License at
110812 *
110813 * http://www.apache.org/licenses/LICENSE-2.0
110814 *
110815 * Unless required by applicable law or agreed to in writing, software
110816 * distributed under the License is distributed on an "AS IS" BASIS,
110817 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
110818 * See the License for the specific language governing permissions and
110819 * limitations under the License.
110820 */
110821
110822 namespace JMS\Serializer\Tests\Metadata\Driver;
110823
110824 use Doctrine\Common\Annotations\AnnotationReader;
110825 use JMS\Serializer\Metadata\Driver\AnnotationDriver;
110826
110827 class AnnotationDriverTest extends BaseDriverTest
110828 {
110829 protected function getDriver()
110830 {
110831 return new AnnotationDriver(new AnnotationReader());
110832 }
110833 }<?php
110834
110835 /*
110836 * Copyright 2013 Johannes M. Schmitt <schmittjoh@gmail.com>
110837 *
110838 * Licensed under the Apache License, Version 2.0 (the "License");
110839 * you may not use this file except in compliance with the License.
110840 * You may obtain a copy of the License at
110841 *
110842 * http://www.apache.org/licenses/LICENSE-2.0
110843 *
110844 * Unless required by applicable law or agreed to in writing, software
110845 * distributed under the License is distributed on an "AS IS" BASIS,
110846 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
110847 * See the License for the specific language governing permissions and
110848 * limitations under the License.
110849 */
110850
110851 namespace JMS\Serializer\Tests\Metadata\Driver;
110852
110853 use JMS\Serializer\Metadata\Driver\AnnotationDriver;
110854 use JMS\Serializer\Metadata\Driver\DoctrineTypeDriver;
110855 use Doctrine\Common\Annotations\AnnotationReader;
110856 use Doctrine\ORM\Configuration;
110857 use Doctrine\ORM\EntityManager;
110858 use Doctrine\ORM\Mapping\Driver\AnnotationDriver as DoctrineDriver;
110859
110860 class DoctrineDriverTest extends \PHPUnit_Framework_TestCase
110861 {
110862 public function getMetadata()
110863 {
110864 $refClass = new \ReflectionClass('JMS\Serializer\Tests\Fixtures\Doctrine\BlogPost');
110865 $metadata = $this->getDoctrineDriver()->loadMetadataForClass($refClass);
110866
110867 return $metadata;
110868 }
110869
110870 public function testTypelessPropertyIsGivenTypeFromDoctrineMetadata()
110871 {
110872 $metadata = $this->getMetadata();
110873
110874 $this->assertEquals(
110875 array('name'=> 'DateTime', 'params' => array()),
110876 $metadata->propertyMetadata['createdAt']->type
110877 );
110878 }
110879
110880 public function testSingleValuedAssociationIsProperlyHinted()
110881 {
110882 $metadata = $this->getMetadata();
110883 $this->assertEquals(
110884 array('name'=> 'JMS\Serializer\Tests\Fixtures\Doctrine\Author', 'params' => array()),
110885 $metadata->propertyMetadata['author']->type
110886 );
110887 }
110888
110889 public function testMultiValuedAssociationIsProperlyHinted()
110890 {
110891 $metadata = $this->getMetadata();
110892
110893 $this->assertEquals(
110894 array('name'=> 'ArrayCollection', 'params' => array(
110895 array('name' => 'JMS\Serializer\Tests\Fixtures\Doctrine\Comment', 'params' => array()))
110896 ),
110897 $metadata->propertyMetadata['comments']->type
110898 );
110899 }
110900
110901 public function testTypeGuessByDoctrineIsOverwrittenByDelegateDriver()
110902 {
110903 $metadata = $this->getMetadata();
110904
110905 // This would be guessed as boolean but we've overriden it to integer
110906 $this->assertEquals(
110907 array('name'=> 'integer', 'params' => array()),
110908 $metadata->propertyMetadata['published']->type
110909 );
110910 }
110911
110912 public function testUnknownDoctrineTypeDoesNotResultInAGuess()
110913 {
110914 $metadata = $this->getMetadata();
110915 $this->assertNull($metadata->propertyMetadata['slug']->type);
110916 }
110917
110918 public function testNonDoctrineEntityClassIsNotModified()
110919 {
110920 // Note: Using regular BlogPost fixture here instead of Doctrine fixture
110921 // because it has no Doctrine metadata.
110922 $refClass = new \ReflectionClass('JMS\Serializer\Tests\Fixtures\BlogPost');
110923
110924 $plainMetadata = $this->getAnnotationDriver()->loadMetadataForClass($refClass);
110925 $doctrineMetadata = $this->getDoctrineDriver()->loadMetadataForClass($refClass);
110926
110927 $this->assertEquals($plainMetadata, $doctrineMetadata);
110928 }
110929
110930 protected function getEntityManager()
110931 {
110932 $config = new Configuration();
110933 $config->setProxyDir(sys_get_temp_dir() . '/JMSDoctrineTestProxies');
110934 $config->setProxyNamespace('JMS\Tests\Proxies');
110935 $config->setMetadataDriverImpl(
110936 new DoctrineDriver(new AnnotationReader(), __DIR__.'/../../Fixtures/Doctrine')
110937 );
110938
110939 $conn = array(
110940 'driver' => 'pdo_sqlite',
110941 'memory' => true,
110942 );
110943
110944 return EntityManager::create($conn, $config);
110945 }
110946
110947 public function getAnnotationDriver()
110948 {
110949 return new AnnotationDriver(new AnnotationReader());
110950 }
110951
110952 protected function getDoctrineDriver()
110953 {
110954 $registry = $this->getMock('Doctrine\Common\Persistence\ManagerRegistry');
110955 $registry->expects($this->atLeastOnce())
110956 ->method('getManagerForClass')
110957 ->will($this->returnValue($this->getEntityManager()));
110958
110959 return new DoctrineTypeDriver(
110960 $this->getAnnotationDriver(),
110961 $registry
110962 );
110963 }
110964 }
110965 JMS\Serializer\Tests\Fixtures\AuthorReadOnly:
110966 xml_root_name: author
110967 properties:
110968 id:
110969 read_only: true
110970 name:
110971 serialized_name: full_name
110972 access_type: public_method
110973 accessor_getter: getName
110974 read_only: true
110975 JMS\Serializer\Tests\Fixtures\ObjectWithVirtualProperties:
110976 properties:
110977 existField:
110978 type: string
110979 virtual_properties:
110980 getVirtualValue: ~
110981 getVirtualSerializedValue:
110982 serialized_name: test
110983 getTypedVirtualProperty:
110984 type: integer
110985 JMS\Serializer\Tests\Fixtures\BlogPost:
110986 xml_root_name: blog-post
110987 properties:
110988 title:
110989 accessor:
110990 getter: getOtherTitle
110991 setter: setOtherTitle
110992 JMS\Serializer\Tests\Fixtures\Node:
110993 properties:
110994 children:
110995 max_depth: 2
110996 JMS\Serializer\Tests\Fixtures\Discriminator\Car: { }JMS\Serializer\Tests\Fixtures\BlogPost:
110997 xml_root_name: blog-post
110998 properties:
110999 title:
111000 type: string
111001 groups: [comments, post]
111002 createdAt:
111003 type: DateTime
111004 xml_attribute: true
111005 published:
111006 type: boolean
111007 serialized_name: is_published
111008 xml_attribute: true
111009 groups: [post]
111010 comments:
111011 type: ArrayCollection<JMS\Serializer\Tests\Fixtures\Comment>
111012 groups: [comments]
111013 xml_list:
111014 inline: true
111015 entry_name: comment
111016 author:
111017 type: JMS\Serializer\Tests\Fixtures\Author
111018 groups: [post]JMS\Serializer\Tests\Fixtures\ObjectWithVirtualPropertiesAndExcludeAll:
111019 exclusion_policy: all
111020 virtual_properties:
111021 getVirtualValue: ~JMS\Serializer\Tests\Fixtures\ObjectWithXmlKeyValuePairs:
111022 properties:
111023 array:
111024 type: array
111025 xml_key_value_pairs: true
111026 JMS\Serializer\Tests\Fixtures\BlogPost:
111027 xml_root_name: blog-post
111028 exclusion_policy: ALL
111029 properties:
111030 title:
111031 type: string
111032 expose: true
111033 JMS\Serializer\Tests\Fixtures\Discriminator\Vehicle:
111034 discriminator:
111035 field_name: type
111036 map:
111037 car: JMS\Serializer\Tests\Fixtures\Discriminator\Car
111038 moped: JMS\Serializer\Tests\Fixtures\Discriminator\Moped
111039
111040 properties:
111041 km:
111042 type: integerJMS\Serializer\Tests\Fixtures\BlogPost:
111043 xml_root_name: blog-post
111044 exclusion_policy: NONE
111045 properties:
111046 title:
111047 type: string
111048 exclude: true
111049 JMS\Serializer\Tests\Fixtures\BlogPost:
111050 xml_root_name: blog-post
111051 exclusion_policy: all
111052 properties:
111053 title:
111054 type: string
111055 expose: TRUE
111056 JMS\Serializer\Tests\Fixtures\Price:
111057 properties:
111058 price:
111059 type: double
111060 xml_value: trueJMS\Serializer\Tests\Fixtures\Discriminator\Moped: { }<?php
111061
111062 /*
111063 * Copyright 2013 Johannes M. Schmitt <schmittjoh@gmail.com>
111064 *
111065 * Licensed under the Apache License, Version 2.0 (the "License");
111066 * you may not use this file except in compliance with the License.
111067 * You may obtain a copy of the License at
111068 *
111069 * http://www.apache.org/licenses/LICENSE-2.0
111070 *
111071 * Unless required by applicable law or agreed to in writing, software
111072 * distributed under the License is distributed on an "AS IS" BASIS,
111073 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
111074 * See the License for the specific language governing permissions and
111075 * limitations under the License.
111076 */
111077
111078 namespace JMS\Serializer\Tests\Metadata\Driver;
111079
111080 use Metadata\Driver\FileLocator;
111081 use JMS\Serializer\Metadata\Driver\PhpDriver;
111082
111083 class PhpDriverTest extends BaseDriverTest
111084 {
111085 protected function getDriver()
111086 {
111087 return new PhpDriver(new FileLocator(array(
111088 'JMS\Serializer\Tests\Fixtures' => __DIR__.'/php',
111089 )));
111090 }
111091 }<?php
111092
111093 /*
111094 * Copyright 2013 Johannes M. Schmitt <schmittjoh@gmail.com>
111095 *
111096 * Licensed under the Apache License, Version 2.0 (the "License");
111097 * you may not use this file except in compliance with the License.
111098 * You may obtain a copy of the License at
111099 *
111100 * http://www.apache.org/licenses/LICENSE-2.0
111101 *
111102 * Unless required by applicable law or agreed to in writing, software
111103 * distributed under the License is distributed on an "AS IS" BASIS,
111104 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
111105 * See the License for the specific language governing permissions and
111106 * limitations under the License.
111107 */
111108
111109 namespace JMS\Serializer\Tests\Metadata\Driver;
111110
111111 use JMS\Serializer\Metadata\ClassMetadata;
111112 use JMS\Serializer\Metadata\PropertyMetadata;
111113 use JMS\Serializer\Metadata\VirtualPropertyMetadata;
111114 use Metadata\Driver\DriverInterface;
111115
111116 abstract class BaseDriverTest extends \PHPUnit_Framework_TestCase
111117 {
111118 public function testLoadBlogPostMetadata()
111119 {
111120 $m = $this->getDriver()->loadMetadataForClass(new \ReflectionClass('JMS\Serializer\Tests\Fixtures\BlogPost'));
111121
111122 $this->assertNotNull($m);
111123 $this->assertEquals('blog-post', $m->xmlRootName);
111124
111125 $p = new PropertyMetadata($m->name, 'title');
111126 $p->type = array('name' => 'string', 'params' => array());
111127 $p->groups = array("comments","post");
111128 $this->assertEquals($p, $m->propertyMetadata['title']);
111129
111130 $p = new PropertyMetadata($m->name, 'createdAt');
111131 $p->type = array('name' => 'DateTime', 'params' => array());
111132 $p->xmlAttribute = true;
111133 $this->assertEquals($p, $m->propertyMetadata['createdAt']);
111134
111135 $p = new PropertyMetadata($m->name, 'published');
111136 $p->type = array('name' => 'boolean', 'params' => array());
111137 $p->serializedName = 'is_published';
111138 $p->xmlAttribute = true;
111139 $p->groups = array("post");
111140 $this->assertEquals($p, $m->propertyMetadata['published']);
111141
111142 $p = new PropertyMetadata($m->name, 'comments');
111143 $p->type = array('name' => 'ArrayCollection', 'params' => array(array('name' => 'JMS\Serializer\Tests\Fixtures\Comment', 'params' => array())));
111144 $p->xmlCollection = true;
111145 $p->xmlCollectionInline = true;
111146 $p->xmlEntryName = 'comment';
111147 $p->groups = array("comments");
111148 $this->assertEquals($p, $m->propertyMetadata['comments']);
111149
111150 $p = new PropertyMetadata($m->name, 'author');
111151 $p->type = array('name' => 'JMS\Serializer\Tests\Fixtures\Author', 'params' => array());
111152 $p->groups = array("post");
111153 $this->assertEquals($p, $m->propertyMetadata['author']);
111154
111155 $m = $this->getDriver()->loadMetadataForClass(new \ReflectionClass('JMS\Serializer\Tests\Fixtures\Price'));
111156 $this->assertNotNull($m);
111157
111158 $p = new PropertyMetadata($m->name, 'price');
111159 $p->type = array('name' => 'double', 'params' => array());
111160 $p->xmlValue = true;
111161 $this->assertEquals($p, $m->propertyMetadata['price']);
111162 }
111163
111164 public function testVirtualProperty()
111165 {
111166 $m = $this->getDriver()->loadMetadataForClass(new \ReflectionClass('JMS\Serializer\Tests\Fixtures\ObjectWithVirtualProperties'));
111167
111168 $this->assertArrayHasKey('existField', $m->propertyMetadata);
111169 $this->assertArrayHasKey('virtualValue', $m->propertyMetadata);
111170 $this->assertArrayHasKey('virtualSerializedValue', $m->propertyMetadata);
111171 $this->assertArrayHasKey('typedVirtualProperty', $m->propertyMetadata);
111172
111173 $this->assertEquals($m->propertyMetadata['virtualSerializedValue']->serializedName, 'test', 'Serialized name is missing' );
111174
111175 $p = new VirtualPropertyMetadata($m->name, 'virtualValue');
111176 $p->getter = 'getVirtualValue';
111177
111178 $this->assertEquals($p, $m->propertyMetadata['virtualValue']);
111179 }
111180
111181 public function testXmlKeyValuePairs()
111182 {
111183 $m = $this->getDriver()->loadMetadataForClass(new \ReflectionClass('JMS\Serializer\Tests\Fixtures\ObjectWithXmlKeyValuePairs'));
111184
111185 $this->assertArrayHasKey('array', $m->propertyMetadata);
111186 $this->assertTrue($m->propertyMetadata['array']->xmlKeyValuePairs);
111187 }
111188
111189 public function testVirtualPropertyWithExcludeAll()
111190 {
111191 $a = new \JMS\Serializer\Tests\Fixtures\ObjectWithVirtualPropertiesAndExcludeAll();
111192 $m = $this->getDriver()->loadMetadataForClass(new \ReflectionClass($a));
111193
111194 $this->assertArrayHasKey('virtualValue', $m->propertyMetadata);
111195
111196 $p = new VirtualPropertyMetadata($m->name, 'virtualValue');
111197 $p->getter = 'getVirtualValue';
111198
111199 $this->assertEquals($p, $m->propertyMetadata['virtualValue']);
111200 }
111201
111202 public function testReadOnlyDefinedBeforeGetterAndSetter()
111203 {
111204 $m = $this->getDriver()->loadMetadataForClass(new \ReflectionClass('JMS\Serializer\Tests\Fixtures\AuthorReadOnly'));
111205
111206 $this->assertNotNull($m);
111207 }
111208
111209 public function testLoadDiscriminator()
111210 {
111211 /** @var $m ClassMetadata */
111212 $m = $this->getDriver()->loadMetadataForClass(new \ReflectionClass('JMS\Serializer\Tests\Fixtures\Discriminator\Vehicle'));
111213
111214 $this->assertNotNull($m);
111215 $this->assertEquals('type', $m->discriminatorFieldName);
111216 $this->assertEquals($m->name, $m->discriminatorBaseClass);
111217 $this->assertEquals(
111218 array(
111219 'car' => 'JMS\Serializer\Tests\Fixtures\Discriminator\Car',
111220 'moped' => 'JMS\Serializer\Tests\Fixtures\Discriminator\Moped',
111221 ),
111222 $m->discriminatorMap
111223 );
111224 }
111225
111226 public function testLoadDiscriminatorSubClass()
111227 {
111228 /** @var $m ClassMetadata */
111229 $m = $this->getDriver()->loadMetadataForClass(new \ReflectionClass('JMS\Serializer\Tests\Fixtures\Discriminator\Car'));
111230
111231 $this->assertNotNull($m);
111232 $this->assertNull($m->discriminatorValue);
111233 $this->assertNull($m->discriminatorBaseClass);
111234 $this->assertNull($m->discriminatorFieldName);
111235 $this->assertEquals(array(), $m->discriminatorMap);
111236 }
111237
111238 public function testMaxDepth()
111239 {
111240 $m = $this->getDriver()->loadMetadataForClass(new \ReflectionClass('JMS\Serializer\Tests\Fixtures\Node'));
111241
111242 $this->assertEquals(2, $m->propertyMetadata['children']->maxDepth);
111243 }
111244
111245 /**
111246 * @return DriverInterface
111247 */
111248 abstract protected function getDriver();
111249 }
111250 <?php
111251
111252 use JMS\Serializer\Metadata\ClassMetadata;
111253 use JMS\Serializer\Metadata\VirtualPropertyMetadata;
111254
111255 $className = 'JMS\Serializer\Tests\Fixtures\ObjectWithVirtualPropertiesAndExcludeAll';
111256
111257 $metadata = new ClassMetadata( $className );
111258
111259 $pMetadata = new VirtualPropertyMetadata($className, 'virtualValue');
111260 $pMetadata->getter = 'getVirtualValue';
111261 $metadata->addPropertyMetadata($pMetadata);
111262
111263 return $metadata;<?php
111264
111265 use JMS\Serializer\Metadata\ClassMetadata;
111266 use JMS\Serializer\Metadata\PropertyMetadata;
111267
111268 $metadata = new ClassMetadata('JMS\Serializer\Tests\Fixtures\Discriminator\Moped');
111269
111270 return $metadata;<?php
111271
111272 use JMS\Serializer\Metadata\ClassMetadata;
111273 use JMS\Serializer\Metadata\PropertyMetadata;
111274
111275 $metadata = new ClassMetadata('JMS\Serializer\Tests\Fixtures\Discriminator\Car');
111276
111277 return $metadata;<?php
111278
111279 use JMS\Serializer\Metadata\ClassMetadata;
111280
111281 $metadata = new ClassMetadata('JMS\Serializer\Tests\Fixtures\AuthorReadOnly');
111282
111283 return $metadata;<?php
111284 use JMS\Serializer\Metadata\ClassMetadata;
111285 use JMS\Serializer\Metadata\PropertyMetadata;
111286
111287 $className = 'JMS\Serializer\Tests\Fixtures\ObjectWithXmlKeyValuePairs';
111288
111289 $metadata = new ClassMetadata($className);
111290
111291 $pMetadata = new PropertyMetadata($className, 'array');
111292 $pMetadata->xmlKeyValuePairs = true;
111293 $metadata->addPropertyMetadata($pMetadata);
111294
111295 return $metadata;
111296 <?php
111297
111298 use JMS\Serializer\Metadata\ClassMetadata;
111299 use JMS\Serializer\Metadata\PropertyMetadata;
111300
111301 $metadata = new ClassMetadata('JMS\Serializer\Tests\Fixtures\Node');
111302
111303 $pMetadata = new PropertyMetadata('JMS\Serializer\Tests\Fixtures\Node', 'children');
111304 $pMetadata->maxDepth = 2;
111305 $metadata->addPropertyMetadata($pMetadata);
111306
111307 return $metadata;
111308 <?php
111309
111310 use JMS\Serializer\Metadata\ClassMetadata;
111311 use JMS\Serializer\Metadata\PropertyMetadata;
111312
111313 $metadata = new ClassMetadata('JMS\Serializer\Tests\Fixtures\BlogPost');
111314 $metadata->xmlRootName = 'blog-post';
111315
111316 $pMetadata = new PropertyMetadata('JMS\Serializer\Tests\Fixtures\BlogPost', 'title');
111317 $pMetadata->setType('string');
111318 $pMetadata->groups = array('comments','post');
111319 $metadata->addPropertyMetadata($pMetadata);
111320
111321 $pMetadata = new PropertyMetadata('JMS\Serializer\Tests\Fixtures\BlogPost', 'createdAt');
111322 $pMetadata->setType('DateTime');
111323 $pMetadata->xmlAttribute = true;
111324 $metadata->addPropertyMetadata($pMetadata);
111325
111326 $pMetadata = new PropertyMetadata('JMS\Serializer\Tests\Fixtures\BlogPost', 'published');
111327 $pMetadata->setType('boolean');
111328 $pMetadata->serializedName = 'is_published';
111329 $pMetadata->groups = array('post');
111330 $pMetadata->xmlAttribute = true;
111331 $metadata->addPropertyMetadata($pMetadata);
111332
111333 $pMetadata = new PropertyMetadata('JMS\Serializer\Tests\Fixtures\BlogPost', 'comments');
111334 $pMetadata->setType('ArrayCollection<JMS\Serializer\Tests\Fixtures\Comment>');
111335 $pMetadata->xmlCollection = true;
111336 $pMetadata->xmlCollectionInline = true;
111337 $pMetadata->xmlEntryName = 'comment';
111338 $pMetadata->groups = array('comments');
111339
111340 $metadata->addPropertyMetadata($pMetadata);
111341
111342 $pMetadata = new PropertyMetadata('JMS\Serializer\Tests\Fixtures\BlogPost', 'author');
111343 $pMetadata->setType('JMS\Serializer\Tests\Fixtures\Author');
111344 $pMetadata->groups = array('post');
111345
111346 $metadata->addPropertyMetadata($pMetadata);
111347
111348 return $metadata;<?php
111349
111350 use JMS\Serializer\Metadata\ClassMetadata;
111351 use JMS\Serializer\Metadata\PropertyMetadata;
111352
111353 $metadata = new ClassMetadata('JMS\Serializer\Tests\Fixtures\Discriminator\Vehicle');
111354 $metadata->setDiscriminator('type', array(
111355 'car' => 'JMS\Serializer\Tests\Fixtures\Discriminator\Car',
111356 'moped' => 'JMS\Serializer\Tests\Fixtures\Discriminator\Moped',
111357 ));
111358
111359 $km = new PropertyMetadata('JMS\Serializer\Tests\Fixtures\Discriminator\Vehicle', 'km');
111360 $km->setType('integer');
111361 $metadata->addPropertyMetadata($km);
111362
111363 return $metadata;<?php
111364
111365 use JMS\Serializer\Metadata\ClassMetadata;
111366 use JMS\Serializer\Metadata\PropertyMetadata;
111367
111368 $metadata = new ClassMetadata('JMS\Serializer\Tests\Fixtures\Price');
111369
111370 $pMetadata = new PropertyMetadata('JMS\Serializer\Tests\Fixtures\Price', 'price');
111371 $pMetadata->setType('double');
111372 $pMetadata->xmlValue = true;
111373 $metadata->addPropertyMetadata($pMetadata);
111374
111375 return $metadata;<?php
111376
111377 use JMS\Serializer\Metadata\ClassMetadata;
111378 use JMS\Serializer\Metadata\PropertyMetadata;
111379 use JMS\Serializer\Metadata\VirtualPropertyMetadata;
111380
111381 $className = 'JMS\Serializer\Tests\Fixtures\ObjectWithVirtualProperties';
111382
111383 $metadata = new ClassMetadata( $className );
111384
111385 $pMetadata = new PropertyMetadata($className, 'existField');
111386 $metadata->addPropertyMetadata($pMetadata);
111387
111388 $pMetadata = new VirtualPropertyMetadata($className, 'virtualValue');
111389 $pMetadata->getter = 'getVirtualValue';
111390 $metadata->addPropertyMetadata($pMetadata);
111391
111392
111393 $pMetadata = new VirtualPropertyMetadata($className, 'virtualSerializedValue');
111394 $pMetadata->getter = 'getVirtualSerializedValue';
111395 $pMetadata->serializedName = 'test';
111396 $metadata->addPropertyMetadata($pMetadata);
111397
111398 $pMetadata = new VirtualPropertyMetadata($className, 'typedVirtualProperty');
111399 $pMetadata->getter = 'getTypedVirtualProperty';
111400 $pMetadata->setType('integer');
111401 $metadata->addPropertyMetadata($pMetadata);
111402
111403 return $metadata;
111404 <?php
111405
111406 /*
111407 * Copyright 2013 Johannes M. Schmitt <schmittjoh@gmail.com>
111408 *
111409 * Licensed under the Apache License, Version 2.0 (the "License");
111410 * you may not use this file except in compliance with the License.
111411 * You may obtain a copy of the License at
111412 *
111413 * http://www.apache.org/licenses/LICENSE-2.0
111414 *
111415 * Unless required by applicable law or agreed to in writing, software
111416 * distributed under the License is distributed on an "AS IS" BASIS,
111417 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
111418 * See the License for the specific language governing permissions and
111419 * limitations under the License.
111420 */
111421
111422 namespace JMS\Serializer\Tests\Metadata\Driver;
111423
111424 use Metadata\Driver\FileLocator;
111425 use JMS\Serializer\Metadata\PropertyMetadata;
111426 use JMS\Serializer\Metadata\Driver\YamlDriver;
111427
111428 class YamlDriverTest extends BaseDriverTest
111429 {
111430 public function testBlogPostExcludeAllStrategy()
111431 {
111432 $m = $this->getDriver('exclude_all')->loadMetadataForClass(new \ReflectionClass('JMS\Serializer\Tests\Fixtures\BlogPost'));
111433
111434 $this->assertArrayHasKey('title', $m->propertyMetadata);
111435
111436 $excluded = array('createdAt', 'published', 'comments', 'author');
111437 foreach ($excluded as $key) {
111438 $this->assertArrayNotHasKey($key, $m->propertyMetadata);
111439 }
111440 }
111441
111442 public function testBlogPostExcludeNoneStrategy()
111443 {
111444 $m = $this->getDriver('exclude_none')->loadMetadataForClass(new \ReflectionClass('JMS\Serializer\Tests\Fixtures\BlogPost'));
111445
111446 $this->assertArrayNotHasKey('title', $m->propertyMetadata);
111447
111448 $excluded = array('createdAt', 'published', 'comments', 'author');
111449 foreach ($excluded as $key) {
111450 $this->assertArrayHasKey($key, $m->propertyMetadata);
111451 }
111452 }
111453
111454 public function testBlogPostCaseInsensitive()
111455 {
111456 $m = $this->getDriver('case')->loadMetadataForClass(new \ReflectionClass('JMS\Serializer\Tests\Fixtures\BlogPost'));
111457
111458 $p = new PropertyMetadata($m->name, 'title');
111459 $p->type = array('name' => 'string', 'params' => array());
111460 $this->assertEquals($p, $m->propertyMetadata['title']);
111461 }
111462
111463 public function testBlogPostAccessor()
111464 {
111465 $m = $this->getDriver('accessor')->loadMetadataForClass(new \ReflectionClass('JMS\Serializer\Tests\Fixtures\BlogPost'));
111466
111467 $this->assertArrayHasKey('title', $m->propertyMetadata);
111468
111469 $p = new PropertyMetadata($m->name, 'title');
111470 $p->getter = 'getOtherTitle';
111471 $p->setter = 'setOtherTitle';
111472 $this->assertEquals($p, $m->propertyMetadata['title']);
111473 }
111474
111475 protected function getDriver()
111476 {
111477 $append = '';
111478 if (func_num_args() == 1) {
111479 $append = '/'.func_get_arg(0);
111480 }
111481
111482 return new YamlDriver(new FileLocator(array(
111483 'JMS\Serializer\Tests\Fixtures' => __DIR__.'/yml'.$append,
111484 )));
111485 }
111486 }<?php
111487
111488 /*
111489 * Copyright 2013 Johannes M. Schmitt <schmittjoh@gmail.com>
111490 *
111491 * Licensed under the Apache License, Version 2.0 (the "License");
111492 * you may not use this file except in compliance with the License.
111493 * You may obtain a copy of the License at
111494 *
111495 * http://www.apache.org/licenses/LICENSE-2.0
111496 *
111497 * Unless required by applicable law or agreed to in writing, software
111498 * distributed under the License is distributed on an "AS IS" BASIS,
111499 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
111500 * See the License for the specific language governing permissions and
111501 * limitations under the License.
111502 */
111503
111504 namespace JMS\Serializer\Tests\Metadata\Driver;
111505
111506 use Metadata\Driver\FileLocator;
111507 use JMS\Serializer\Metadata\PropertyMetadata;
111508 use JMS\Serializer\Metadata\Driver\XmlDriver;
111509
111510 class XmlDriverTest extends BaseDriverTest
111511 {
111512 /**
111513 * @expectedException JMS\Serializer\Exception\XmlErrorException
111514 * @expectedExceptionMessage [FATAL] Start tag expected, '<' not found
111515 */
111516 public function testInvalidXml()
111517 {
111518 $driver = $this->getDriver();
111519
111520 $ref = new \ReflectionMethod($driver, 'loadMetadataFromFile');
111521 $ref->setAccessible(true);
111522 $ref->invoke($driver, new \ReflectionClass('stdClass'), __DIR__.'/xml/invalid.xml');
111523 }
111524
111525 public function testBlogPostExcludeAllStrategy()
111526 {
111527 $m = $this->getDriver('exclude_all')->loadMetadataForClass(new \ReflectionClass('JMS\Serializer\Tests\Fixtures\BlogPost'));
111528
111529 $this->assertArrayHasKey('title', $m->propertyMetadata);
111530
111531 $excluded = array('createdAt', 'published', 'comments', 'author');
111532 foreach ($excluded as $key) {
111533 $this->assertArrayNotHasKey($key, $m->propertyMetadata);
111534 }
111535 }
111536
111537 public function testBlogPostExcludeNoneStrategy()
111538 {
111539 $m = $this->getDriver('exclude_none')->loadMetadataForClass(new \ReflectionClass('JMS\Serializer\Tests\Fixtures\BlogPost'));
111540
111541 $this->assertArrayNotHasKey('title', $m->propertyMetadata);
111542
111543 $excluded = array('createdAt', 'published', 'comments', 'author');
111544 foreach ($excluded as $key) {
111545 $this->assertArrayHasKey($key, $m->propertyMetadata);
111546 }
111547 }
111548
111549 public function testBlogPostCaseInsensitive()
111550 {
111551 $m = $this->getDriver('case')->loadMetadataForClass(new \ReflectionClass('JMS\Serializer\Tests\Fixtures\BlogPost'));
111552
111553 $p = new PropertyMetadata($m->name, 'title');
111554 $p->type = array('name' => 'string', 'params' => array());
111555 $this->assertEquals($p, $m->propertyMetadata['title']);
111556 }
111557
111558 public function testAccessorAttributes()
111559 {
111560 $m = $this->getDriver()->loadMetadataForClass(new \ReflectionClass('JMS\Serializer\Tests\Fixtures\GetSetObject'));
111561
111562 $p = new PropertyMetadata($m->name, 'name');
111563 $p->type = array('name' => 'string', 'params' => array());
111564 $p->getter = 'getTrimmedName';
111565 $p->setter = 'setCapitalizedName';
111566
111567 $this->assertEquals($p, $m->propertyMetadata['name']);
111568 }
111569
111570 protected function getDriver()
111571 {
111572 $append = '';
111573 if (func_num_args() == 1) {
111574 $append = '/'.func_get_arg(0);
111575 }
111576 return new XmlDriver(new FileLocator(array(
111577 'JMS\Serializer\Tests\Fixtures' => __DIR__.'/xml'.$append,
111578 )));
111579 }
111580 }<?php
111581
111582 /*
111583 * Copyright 2013 Johannes M. Schmitt <schmittjoh@gmail.com>
111584 *
111585 * Licensed under the Apache License, Version 2.0 (the "License");
111586 * you may not use this file except in compliance with the License.
111587 * You may obtain a copy of the License at
111588 *
111589 * http://www.apache.org/licenses/LICENSE-2.0
111590 *
111591 * Unless required by applicable law or agreed to in writing, software
111592 * distributed under the License is distributed on an "AS IS" BASIS,
111593 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
111594 * See the License for the specific language governing permissions and
111595 * limitations under the License.
111596 */
111597
111598 namespace JMS\Serializer\Tests\Metadata;
111599
111600 use JMS\Serializer\Metadata\PropertyMetadata;
111601 use JMS\Serializer\Metadata\ClassMetadata;
111602
111603 class ClassMetadataTest extends \PHPUnit_Framework_TestCase
111604 {
111605 public function testSetAccessorOrder()
111606 {
111607 $metadata = new ClassMetadata('JMS\Serializer\Tests\Metadata\PropertyMetadataOrder');
111608 $metadata->addPropertyMetadata(new PropertyMetadata('JMS\Serializer\Tests\Metadata\PropertyMetadataOrder', 'b'));
111609 $metadata->addPropertyMetadata(new PropertyMetadata('JMS\Serializer\Tests\Metadata\PropertyMetadataOrder', 'a'));
111610 $this->assertEquals(array('b', 'a'), array_keys($metadata->propertyMetadata));
111611
111612 $metadata->setAccessorOrder(ClassMetadata::ACCESSOR_ORDER_ALPHABETICAL);
111613 $this->assertEquals(array('a', 'b'), array_keys($metadata->propertyMetadata));
111614
111615 $metadata->setAccessorOrder(ClassMetadata::ACCESSOR_ORDER_CUSTOM, array('b', 'a'));
111616 $this->assertEquals(array('b', 'a'), array_keys($metadata->propertyMetadata));
111617
111618 $metadata->setAccessorOrder(ClassMetadata::ACCESSOR_ORDER_CUSTOM, array('a', 'b'));
111619 $this->assertEquals(array('a', 'b'), array_keys($metadata->propertyMetadata));
111620
111621 $metadata->setAccessorOrder(ClassMetadata::ACCESSOR_ORDER_CUSTOM, array('b'));
111622 $this->assertEquals(array('b', 'a'), array_keys($metadata->propertyMetadata));
111623
111624 $metadata->setAccessorOrder(ClassMetadata::ACCESSOR_ORDER_CUSTOM, array('a'));
111625 $this->assertEquals(array('a', 'b'), array_keys($metadata->propertyMetadata));
111626
111627 $metadata->setAccessorOrder(ClassMetadata::ACCESSOR_ORDER_CUSTOM, array('foo', 'bar'));
111628 $this->assertEquals(array('b', 'a'), array_keys($metadata->propertyMetadata));
111629 }
111630 }
111631
111632 class PropertyMetadataOrder
111633 {
111634 private $b, $a;
111635 }This document details changes between individual versions.
111636
111637 For instructions on how to upgrade from one version to another, please see the dedicated UPGRADING document.
111638
111639 0.12 (tbd)
111640 ----------
111641 - adds, and exposes SerializationContext/DeserializationContext
111642 - adds built-in support for deserialization of polymorphic objects when they have a common base class
111643 - adds serializer.pre_deserialize event
111644 - adds a disjunct exclusion strategy
111645 - allows @Type annotation to be used in combination with @VirtualProperty
111646
111647 0.11 (2013-01-29)
111648 -----------------
111649 Initial Release
111650 {
111651 "name": "jms/serializer",
111652 "type": "library",
111653 "description": "Library for (de-)serializing data of any complexity; supports XML, JSON, and YAML.",
111654 "keywords": ["serialization", "deserialization", "json", "jaxb", "xml"],
111655 "homepage": "http://jmsyst.com/libs/serializer",
111656 "license": "Apache2",
111657 "authors": [
111658 {
111659 "name": "Johannes M. Schmitt",
111660 "email": "schmittjoh@gmail.com"
111661 }
111662 ],
111663 "require": {
111664 "php": ">=5.3.2",
111665 "jms/metadata": "~1.1",
111666 "jms/parser-lib": "1.*",
111667 "phpcollection/phpcollection": ">=0.1,<0.3-dev",
111668 "doctrine/annotations": "1.*"
111669 },
111670 "suggest": {
111671 "symfony/yaml": "Required if you'd like to serialize data to YAML format."
111672 },
111673 "minimum-stability": "dev",
111674 "require-dev": {
111675 "twig/twig": ">=1.8,<2.0-dev",
111676 "doctrine/orm": ">=2.1,<2.4-dev",
111677 "symfony/yaml": "2.*",
111678 "symfony/translation": ">=2.0,<2.2-dev",
111679 "symfony/validator": ">=2.0,<2.2-dev",
111680 "symfony/form": ">=2.1,<2.2-dev",
111681 "symfony/filesystem": "2.*"
111682 },
111683 "autoload": {
111684 "psr-0": {
111685 "JMS\\Serializer": "src/"
111686 }
111687 },
111688 "extra": {
111689 "branch-alias": {
111690 "dev-master": "0.13-dev"
111691 }
111692 }
111693 }
111694 <?xml version="1.0" encoding="UTF-8"?>
111695
111696 <phpunit backupGlobals="false"
111697 backupStaticAttributes="false"
111698 convertErrorsToExceptions="true"
111699 convertNoticesToExceptions="true"
111700 convertWarningsToExceptions="true"
111701 processIsolation="false"
111702 stopOnFailure="false"
111703 syntaxCheck="false"
111704 bootstrap="tests/bootstrap.php"
111705 >
111706
111707 <testsuites>
111708 <testsuite name="JMS Serializer Test Suite">
111709 <directory>./tests</directory>
111710 </testsuite>
111711 </testsuites>
111712
111713 <groups>
111714 <exclude>
111715 <group>performance</group>
111716 </exclude>
111717 </groups>
111718 </phpunit>
111719 From 0.11 to 0.12
111720 =================
111721
111722 - GraphNavigator::detachObject has been removed, you can directly use Context::stopVisiting instead.
111723 - VisitorInterface::getNavigator was deprecated, instead use Context::accept
111724 - Serializer::setGroups, Serializer::setExclusionStrategy and Serializer::setVersion were removed, these settings must
111725 now be passed as part of a new Context object.
111726
111727 Before:
111728
111729 $serializer->setVersion(1);
111730 $serializer->serialize($data, 'json');
111731
111732 After:
111733
111734 $serializer->serialize($data, 'json', SerializationContext::create()->setVersion(1));
111735
111736 - All visit??? methods of the VisitorInterface, now require a third argument, the Context; the context is for example
111737 passed as an additional argument to handlers, exclusion strategies, and also available in event listeners.
111738 <?php
111739
111740 // autoload.php @generated by Composer
111741
111742 require_once __DIR__ . '/composer' . '/autoload_real.php';
111743
111744 return ComposerAutoloaderInit09ccc38d6dd411064b9da74608e195df::getLoader();
111745 #!/usr/bin/env php
111746 <?php
111747
111748 call_user_func(function() {
111749 require_once call_user_func(function() {
111750 if (is_file($file = __DIR__.'/../vendor/autoload.php')) {
111751 return $file;
111752 }
111753
111754 if (is_file($file = __DIR__.'/../../../autoload.php')) {
111755 return $file;
111756 }
111757
111758 throw new \LogicException('Could not locate autoload.php. Did you forget to install vendors using "composer install"?');
111759 });
111760
111761 $app = new Scrutinizer\Ocular\Ocular();
111762 $app->run();
111763 });
111764 �����NuzTٿd�k GBMB